AI automatically translates all comments in the code into English (#3917)

This commit is contained in:
alex
2024-09-19 14:53:50 +08:00
committed by GitHub
parent 046de691cb
commit 4152dcd409
279 changed files with 10602 additions and 3038 deletions

View File

@@ -35,9 +35,11 @@ onceToken token([]() {
#else
string ffmpeg_bin = trim(System::execute("which ffmpeg"));
#endif
//默认ffmpeg命令路径为环境变量中路径
// 默认ffmpeg命令路径为环境变量中路径 [AUTO-TRANSLATED:40c35597]
// Default ffmpeg command path is the path in the environment variable
mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin;
//ffmpeg日志保存路径
// ffmpeg日志保存路径 [AUTO-TRANSLATED:e455732d]
// ffmpeg log save path
mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log";
mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kSnap] = "%s -i %s -y -f mjpeg -frames:v 1 -an %s";
@@ -104,7 +106,8 @@ void FFmpegSource::play(const string &ffmpeg_cmd_key, const string &src_url, con
InfoL << cmd;
if (is_local_ip(_media_info.host)) {
// 推流给自己的,通过判断流是否注册上来判断是否正常
// 推流给自己的,通过判断流是否注册上来判断是否正常 [AUTO-TRANSLATED:423f2be6]
// Push stream to yourself, judge whether the stream is registered to determine whether it is normal
if (_media_info.schema != RTSP_SCHEMA && _media_info.schema != RTMP_SCHEMA) {
cb(SockException(Err_other, "本服务只支持rtmp/rtsp推流"));
return;
@@ -113,41 +116,50 @@ void FFmpegSource::play(const string &ffmpeg_cmd_key, const string &src_url, con
findAsync(timeout_ms, [cb, weakSelf, timeout_ms](const MediaSource::Ptr &src) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 自己已经销毁
// 自己已经销毁 [AUTO-TRANSLATED:3d45c3b0]
// Self has been destroyed
return;
}
if (src) {
// 推流给自己成功
// 推流给自己成功 [AUTO-TRANSLATED:65dba71b]
// Push stream to yourself successfully
cb(SockException());
strongSelf->onGetMediaSource(src);
strongSelf->startTimer(timeout_ms);
return;
}
//推流失败
// 推流失败 [AUTO-TRANSLATED:4d8d226a]
// Push stream failed
if (!strongSelf->_process.wait(false)) {
// ffmpeg进程已经退出
// ffmpeg进程已经退出 [AUTO-TRANSLATED:04193893]
// ffmpeg process has exited
cb(SockException(Err_other, StrPrinter << "ffmpeg已经退出,exit code = " << strongSelf->_process.exit_code()));
return;
}
// ffmpeg进程还在线但是等待推流超时
// ffmpeg进程还在线但是等待推流超时 [AUTO-TRANSLATED:9f71f17b]
// ffmpeg process is still online, but waiting for the stream to timeout
cb(SockException(Err_other, "等待超时"));
});
} else{
//推流给其他服务器的通过判断FFmpeg进程是否在线判断是否成功
// 推流给其他服务器的通过判断FFmpeg进程是否在线判断是否成功 [AUTO-TRANSLATED:9b963da5]
// Push stream to other servers, judge whether it is successful by judging whether the FFmpeg process is online
weak_ptr<FFmpegSource> weakSelf = shared_from_this();
_timer = std::make_shared<Timer>(timeout_ms / 1000.0f, [weakSelf, cb, timeout_ms]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 自身已经销毁
// 自身已经销毁 [AUTO-TRANSLATED:5f954f8a]
// Self has been destroyed
return false;
}
// FFmpeg还在线那么我们认为推流成功
// FFmpeg还在线那么我们认为推流成功 [AUTO-TRANSLATED:4330df49]
// FFmpeg is still online, so we think the push stream is successful
if (strongSelf->_process.wait(false)) {
cb(SockException());
strongSelf->startTimer(timeout_ms);
return false;
}
// ffmpeg进程已经退出
// ffmpeg进程已经退出 [AUTO-TRANSLATED:04193893]
// ffmpeg process has exited
cb(SockException(Err_other, StrPrinter << "ffmpeg已经退出,exit code = " << strongSelf->_process.exit_code()));
return false;
}, _poller);
@@ -162,9 +174,11 @@ void FFmpegSource::findAsync(int maxWaitMS, const function<void(const MediaSourc
}
void *listener_tag = this;
// 若干秒后执行等待媒体注册超时回调
// 若干秒后执行等待媒体注册超时回调 [AUTO-TRANSLATED:71010a04]
// Execute the media registration timeout callback after a few seconds
auto onRegistTimeout = _poller->doDelayTask(maxWaitMS, [cb, listener_tag]() {
// 取消监听该事件
// 取消监听该事件 [AUTO-TRANSLATED:31297323]
// Cancel listening to this event
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
cb(nullptr);
return 0;
@@ -174,7 +188,8 @@ void FFmpegSource::findAsync(int maxWaitMS, const function<void(const MediaSourc
auto onRegist = [listener_tag, weakSelf, cb, onRegistTimeout](BroadcastMediaChangedArgs) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 本身已经销毁,取消延时任务
// 本身已经销毁,取消延时任务 [AUTO-TRANSLATED:cc2e420f]
// Self has been destroyed, cancel the delayed task
onRegistTimeout->cancel();
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
return;
@@ -182,29 +197,38 @@ void FFmpegSource::findAsync(int maxWaitMS, const function<void(const MediaSourc
if (!bRegist || sender.getSchema() != strongSelf->_media_info.schema ||
!equalMediaTuple(sender.getMediaTuple(), strongSelf->_media_info)) {
// 不是自己感兴趣的事件,忽略之
// 不是自己感兴趣的事件,忽略之 [AUTO-TRANSLATED:f61f5668]
// Not an event of interest, ignore it
return;
}
// 查找的流终于注册上了;取消延时任务,防止多次回调
// 查找的流终于注册上了;取消延时任务,防止多次回调 [AUTO-TRANSLATED:66fc5abf]
// The stream you are looking for is finally registered; cancel the delayed task to prevent multiple callbacks
onRegistTimeout->cancel();
// 取消事件监听
// 取消事件监听 [AUTO-TRANSLATED:c722acb6]
// Cancel event listening
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
// 切换到自己的线程再回复
// 切换到自己的线程再回复 [AUTO-TRANSLATED:3b630c64]
// Switch to your own thread and then reply
strongSelf->_poller->async([weakSelf, cb]() {
if (auto strongSelf = weakSelf.lock()) {
// 再找一遍媒体源,一般能找到
// 再找一遍媒体源,一般能找到 [AUTO-TRANSLATED:f0b81977]
// Find the media source again, usually you can find it
strongSelf->findAsync(0, cb);
}
}, false);
};
// 监听媒体注册事件
// 监听媒体注册事件 [AUTO-TRANSLATED:ea3e763b]
// Listen to media registration events
NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, onRegist);
}
/**
* 定时检查媒体是否在线
* Check if the media is online regularly
* [AUTO-TRANSLATED:11bae8ab]
*/
void FFmpegSource::startTimer(int timeout_ms) {
weak_ptr<FFmpegSource> weakSelf = shared_from_this();
@@ -212,54 +236,66 @@ void FFmpegSource::startTimer(int timeout_ms) {
_timer = std::make_shared<Timer>(1.0f, [weakSelf, timeout_ms]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
//自身已经销毁
// 自身已经销毁 [AUTO-TRANSLATED:5a02ef8b]
// Self has been destroyed
return false;
}
bool needRestart = ffmpeg_restart_sec > 0 && strongSelf->_replay_ticker.elapsedTime() > ffmpeg_restart_sec * 1000;
if (is_local_ip(strongSelf->_media_info.host)) {
// 推流给自己的我们通过检查是否已经注册来判断FFmpeg是否工作正常
// 推流给自己的我们通过检查是否已经注册来判断FFmpeg是否工作正常 [AUTO-TRANSLATED:9a441d38]
// Push stream to yourself, we judge whether FFmpeg is working properly by checking whether it has been registered
strongSelf->findAsync(0, [&](const MediaSource::Ptr &src) {
// 同步查找流
// 同步查找流 [AUTO-TRANSLATED:97048f1e]
// Synchronously find the stream
if (!src || needRestart) {
if (needRestart) {
strongSelf->_replay_ticker.resetTime();
if (strongSelf->_process.wait(false)) {
// FFmpeg进程还在运行超时就关闭它
// FFmpeg进程还在运行超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, timeout and close it
strongSelf->_process.kill(2000);
}
InfoL << "FFmpeg即将重启, 将会继续拉流 " << strongSelf->_src_url;
}
// 流不在线,重新拉流, 这里原先是10秒超时实际发现10秒不够改成20秒了
// 流不在线,重新拉流, 这里原先是10秒超时实际发现10秒不够改成20秒了 [AUTO-TRANSLATED:10e8c704]
// The stream is not online, re-pull the stream, here the original timeout was 10 seconds, but it was found that 10 seconds was not enough, so it was changed to 20 seconds
if (strongSelf->_replay_ticker.elapsedTime() > 20 * 1000) {
// 上次重试时间超过10秒那么再重试FFmpeg拉流
// 上次重试时间超过10秒那么再重试FFmpeg拉流 [AUTO-TRANSLATED:b308095a]
// The last retry time exceeds 10 seconds, then retry FFmpeg to pull the stream
strongSelf->_replay_ticker.resetTime();
strongSelf->play(strongSelf->_ffmpeg_cmd_key, strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {});
}
}
});
} else {
// 推流给其他服务器的我们通过判断FFmpeg进程是否在线如果FFmpeg推流中断那么它应该会自动退出
// 推流给其他服务器的我们通过判断FFmpeg进程是否在线如果FFmpeg推流中断那么它应该会自动退出 [AUTO-TRANSLATED:82da3ea5]
// Push stream to other servers, we judge whether the FFmpeg process is online, if FFmpeg push stream is interrupted, then it should exit automatically
if (!strongSelf->_process.wait(false) || needRestart) {
if (needRestart) {
strongSelf->_replay_ticker.resetTime();
if (strongSelf->_process.wait(false)) {
// FFmpeg进程还在运行超时就关闭它
// FFmpeg进程还在运行超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, timeout and close it
strongSelf->_process.kill(2000);
}
InfoL << "FFmpeg即将重启, 将会继续拉流 " << strongSelf->_src_url;
}
// ffmpeg不在线重新拉流
// ffmpeg不在线重新拉流 [AUTO-TRANSLATED:aa958c43]
// ffmpeg is not online, re-pull the stream
strongSelf->play(strongSelf->_ffmpeg_cmd_key, strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [weakSelf](const SockException &ex) {
if (!ex) {
// 没有错误
// 没有错误 [AUTO-TRANSLATED:037ae0ca]
// No error
return;
}
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 自身已经销毁
// 自身已经销毁 [AUTO-TRANSLATED:5f954f8a]
// Self has been destroyed
return;
}
// 上次重试时间超过10秒那么再重试FFmpeg拉流
// 上次重试时间超过10秒那么再重试FFmpeg拉流 [AUTO-TRANSLATED:b308095a]
// Retry FFmpeg stream pulling if the last retry time is over 10 seconds
strongSelf->startTimer(10 * 1000);
});
}
@@ -275,10 +311,12 @@ void FFmpegSource::setOnClose(const function<void()> &cb){
bool FFmpegSource::close(MediaSource &sender) {
auto listener = getDelegate();
if (listener && !listener->close(sender)) {
//关闭失败
// 关闭失败 [AUTO-TRANSLATED:83f07dba]
// Close failed
return false;
}
//该流无人观看,我们停止吧
// 该流无人观看,我们停止吧 [AUTO-TRANSLATED:43999b39]
// No one is watching this stream, let's stop it
if (_onClose) {
_onClose();
}
@@ -297,7 +335,8 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) {
auto muxer = src->getMuxer();
auto listener = muxer ? muxer->getDelegate() : nullptr;
if (listener && listener.get() != this) {
//防止多次进入onGetMediaSource函数导致无限递归调用的bug
// 防止多次进入onGetMediaSource函数导致无限递归调用的bug [AUTO-TRANSLATED:ceadb9c7]
// Prevent the bug of infinite recursive calls caused by entering the onGetMediaSource function multiple times
setDelegate(listener);
muxer->setDelegate(shared_from_this());
if (_enable_hls) {
@@ -317,7 +356,8 @@ void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float
WorkThreadPool::Instance().getPoller()->async([timeout_sec, play_url, save_path, cb, ticker]() {
auto elapsed_ms = ticker.elapsedTime();
if (elapsed_ms > timeout_sec * 1000) {
// 超时,后台线程负载太高,当代太久才启动该任务
// 超时,后台线程负载太高,当代太久才启动该任务 [AUTO-TRANSLATED:815606d6]
// Timeout, the background thread load is too high, it takes too long to start this task
cb(false, "wait work poller schedule snap task timeout");
return;
}
@@ -328,21 +368,26 @@ void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float
auto log_file = ffmpeg_log.empty() ? ffmpeg_log : File::absolutePath("", ffmpeg_log);
process->run(cmd, log_file);
//定时器延时应该减去后台任务启动的延时
// 定时器延时应该减去后台任务启动的延时 [AUTO-TRANSLATED:7d224687]
// The timer delay should be reduced by the delay of the background task startup
auto delayTask = EventPollerPool::Instance().getPoller()->doDelayTask(
(uint64_t)(timeout_sec * 1000 - elapsed_ms), [process, cb, log_file, save_path]() {
if (process->wait(false)) {
// FFmpeg进程还在运行超时就关闭它
// FFmpeg进程还在运行超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, close it if it times out
process->kill(2000);
}
return 0;
});
// 等待FFmpeg进程退出
// 等待FFmpeg进程退出 [AUTO-TRANSLATED:0a179187]
// Wait for the FFmpeg process to exit
process->wait(true);
// FFmpeg进程退出了可以取消定时器了
// FFmpeg进程退出了可以取消定时器了 [AUTO-TRANSLATED:c8a4b513]
// The FFmpeg process has exited, the timer can be canceled
delayTask->cancel();
// 执行回调函数
// 执行回调函数 [AUTO-TRANSLATED:7309a900]
// Execute the callback function
bool success = process->exit_code() == 0 && File::fileSize(save_path);
cb(success, (!success && !log_file.empty()) ? File::loadFile(log_file) : "");
});

View File

@@ -26,11 +26,16 @@ namespace FFmpeg {
class FFmpegSnap {
public:
using onSnap = std::function<void(bool success, const std::string &err_msg)>;
/// 创建截图
/// \param play_url 播放url地址只要FFmpeg支持即可
/// \param save_path 截图jpeg文件保存路径
/// \param timeout_sec 生成截图超时时间(防止阻塞太久)
/// \param cb 生成截图成功与否回调
// / 创建截图 [AUTO-TRANSLATED:6d334c49]
// / Create a screenshot
// / \param play_url 播放url地址只要FFmpeg支持即可 [AUTO-TRANSLATED:609d4de4]
// / \param play_url The playback URL address, as long as FFmpeg supports it
// / \param save_path 截图jpeg文件保存路径 [AUTO-TRANSLATED:0fc0ac0d]
// / \param save_path The path to save the screenshot JPEG file
// / \param timeout_sec 生成截图超时时间(防止阻塞太久) [AUTO-TRANSLATED:0dcc0095]
// / \param timeout_sec Timeout for generating the screenshot (to prevent blocking for too long)
// / \param cb 生成截图成功与否回调 [AUTO-TRANSLATED:5b4b93c9]
// / \param cb Callback for whether the screenshot was generated successfully
static void makeSnap(const std::string &play_url, const std::string &save_path, float timeout_sec, const onSnap &cb);
private:
@@ -48,6 +53,9 @@ public:
/**
* 设置主动关闭回调
* Set the active close callback
* [AUTO-TRANSLATED:2134a5b3]
*/
void setOnClose(const std::function<void()> &cb);
@@ -58,6 +66,14 @@ public:
* @param dst_url FFmpeg推流地址
* @param timeout_ms 等待结果超时时间,单位毫秒
* @param cb 成功与否回调
* Start playing the URL
* @param ffmpeg_cmd_key FFmpeg stream command configuration item key, users can set multiple command parameter templates in the configuration file at the same time
* @param src_url FFmpeg stream address
* @param dst_url FFmpeg push stream address
* @param timeout_ms Timeout for waiting for the result, in milliseconds
* @param cb Success or failure callback
* [AUTO-TRANSLATED:2c35789e]
*/
void play(const std::string &ffmpeg_cmd_key, const std::string &src_url, const std::string &dst_url, int timeout_ms, const onPlay &cb);
@@ -65,6 +81,11 @@ public:
* 设置录制
* @param enable_hls 是否开启hls直播或录制
* @param enable_mp4 是否录制mp4
* Set recording
* @param enable_hls Whether to enable HLS live streaming or recording
* @param enable_mp4 Whether to record MP4
* [AUTO-TRANSLATED:9f28d5c2]
*/
void setupRecordFlag(bool enable_hls, bool enable_mp4);
@@ -74,11 +95,14 @@ private:
void onGetMediaSource(const mediakit::MediaSource::Ptr &src);
///////MediaSourceEvent override///////
// 关闭
// 关闭 [AUTO-TRANSLATED:92392f02]
// Close
bool close(mediakit::MediaSource &sender) override;
// 获取媒体源类型
// 获取媒体源类型 [AUTO-TRANSLATED:34290a69]
// Get the media source type
mediakit::MediaOriginType getOriginType(mediakit::MediaSource &sender) const override;
//获取媒体源url或者文件路径
// 获取媒体源url或者文件路径 [AUTO-TRANSLATED:d6d885b8]
// Get the media source URL or file path
std::string getOriginUrl(mediakit::MediaSource &sender) const override;
private:

View File

@@ -35,12 +35,15 @@ using namespace toolkit;
#ifndef _WIN32
static void setupChildProcess() {
//取消cpu亲和性设置防止FFmpeg进程cpu占用率不能超过100%的问题
// 取消cpu亲和性设置防止FFmpeg进程cpu占用率不能超过100%的问题 [AUTO-TRANSLATED:d168129d]
// Cancel CPU affinity settings to prevent FFmpeg process from exceeding 100% CPU usage.
setThreadAffinity(-1);
//子进程关闭core文件生成
// 子进程关闭core文件生成 [AUTO-TRANSLATED:721d2925]
// Disable core file generation for child processes.
struct rlimit rlim = { 0, 0 };
setrlimit(RLIMIT_CORE, &rlim);
//子进程恢复默认信号处理
// 子进程恢复默认信号处理 [AUTO-TRANSLATED:1bc7387b]
// Restore default signal handling for child processes.
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
@@ -52,7 +55,8 @@ static int runChildProcess(string cmd, string log_file) {
setupChildProcess();
if (log_file.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
// 未指定子进程日志文件时,重定向至/dev/null [AUTO-TRANSLATED:dd0c9853]
// Redirect child process logs to /dev/null if no log file is specified.
log_file = "/dev/null";
} else {
log_file = StrPrinter << log_file << "." << getpid();
@@ -64,7 +68,8 @@ static int runChildProcess(string cmd, string log_file) {
open("/dev/null", O_RDONLY, 0666); /* will be fd 0 (STDIN_FILENO) */
}
//重定向shell日志至文件
// 重定向shell日志至文件 [AUTO-TRANSLATED:3480502b]
// Redirect shell logs to file.
auto fp = File::create_file(log_file, "ab");
if (!fp) {
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
@@ -77,7 +82,8 @@ static int runChildProcess(string cmd, string log_file) {
if (dup2(log_fd, STDERR_FILENO) < 0) {
fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
}
// 关闭日志文件
// 关闭日志文件 [AUTO-TRANSLATED:9fb6e256]
// Close log file.
::fclose(fp);
}
fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data());
@@ -114,13 +120,15 @@ void Process::run(const string &cmd, string log_file) {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
if (log_file.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
// 未指定子进程日志文件时,重定向至/dev/null [AUTO-TRANSLATED:dd0c9853]
// Redirect child process logs to /dev/null if no log file is specified.
log_file = "NUL";
} else {
log_file = StrPrinter << log_file << "." << getCurrentMillisecond();
}
//重定向shell日志至文件
// 重定向shell日志至文件 [AUTO-TRANSLATED:3480502b]
// Redirect shell logs to file.
auto fp = File::create_file(log_file, "ab");
if (!fp) {
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
@@ -137,7 +145,8 @@ void Process::run(const string &cmd, string log_file) {
LPTSTR lpDir = const_cast<char *>(cmd.data());
if (CreateProcess(NULL, lpDir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
//下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
// 下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 [AUTO-TRANSLATED:d50fada2]
// The following two lines close the handle and break the relationship between the current process and the new process. Otherwise, the TerminateProcess function may accidentally close the child process.
CloseHandle(pi.hThread);
_pid = pi.dwProcessId;
_handle = pi.hProcess;
@@ -165,12 +174,14 @@ void Process::run(const string &cmd, string log_file) {
throw std::runtime_error(StrPrinter << "fork child process failed, cmd: " << cmd << ",err:" << get_uv_errmsg());
}
if (_pid == 0) {
//子进程
// 子进程 [AUTO-TRANSLATED:3f793797]
// Child process.
exit(runChildProcess(cmd, log_file));
}
#endif
if (log_file.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
// 未指定子进程日志文件时,重定向至/dev/null [AUTO-TRANSLATED:dd0c9853]
// Redirect child process logs to /dev/null if no log file is specified.
log_file = "/dev/null";
} else {
log_file = StrPrinter << log_file << "." << _pid;
@@ -185,6 +196,13 @@ void Process::run(const string &cmd, string log_file) {
* @param exit_code_ptr 进程返回代码
* @param block 是否阻塞等待
* @return 进程是否还在运行
* Get the process's alive status.
* @param pid Process ID
* @param exit_code_ptr Process return code
* @param block Whether to block and wait
* @return Whether the process is still running
* [AUTO-TRANSLATED:ef80ff17]
*/
static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
if (pid <= 0) {
@@ -193,14 +211,16 @@ static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
#ifdef _WIN32
DWORD code = 0;
if (block) {
//一直等待
// 一直等待 [AUTO-TRANSLATED:ca8a5e14]
// Wait indefinitely.
code = WaitForSingleObject(handle, INFINITE);
} else {
code = WaitForSingleObject(handle, 0);
}
if (code == WAIT_FAILED || code == WAIT_OBJECT_0) {
//子进程已经退出了,获取子进程退出代码
// 子进程已经退出了,获取子进程退出代码 [AUTO-TRANSLATED:c39663b2]
// The child process has exited, get the child process exit code.
DWORD exitCode = 0;
if (exit_code_ptr && GetExitCodeProcess(handle, &exitCode)) {
*exit_code_ptr = exitCode;
@@ -209,10 +229,12 @@ static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
}
if (code == WAIT_TIMEOUT) {
//子进程还在线
// 子进程还在线 [AUTO-TRANSLATED:3d32ef56]
// The child process is still online.
return true;
}
//不太可能运行到此处
// 不太可能运行到此处 [AUTO-TRANSLATED:b1fde65d]
// It is unlikely to run to this point.
WarnL << "WaitForSingleObject ret:" << code;
return false;
#else
@@ -268,42 +290,52 @@ bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent) {
static void s_kill(pid_t pid, void *handle, int max_delay, bool force) {
if (pid <= 0) {
// pid无效
// pid无效 [AUTO-TRANSLATED:6665d7a0]
// Invalid pid.
return;
}
#ifdef _WIN32
// windows下目前没有比较好的手段往子进程发送SIGTERM或信号
//所以杀死子进程的方式全部强制为立即关闭
// windows下目前没有比较好的手段往子进程发送SIGTERM或信号 [AUTO-TRANSLATED:cd32ad25]
// Currently, there is no good way to send SIGTERM or signals to child processes under Windows.
// 所以杀死子进程的方式全部强制为立即关闭 [AUTO-TRANSLATED:2fc31f2b]
// Therefore, all methods of killing child processes are forced to be immediate closure.
force = true;
if (force) {
//强制关闭子进程
// 强制关闭子进程 [AUTO-TRANSLATED:f3c712f6]
// Force close the child process.
TerminateProcess(handle, 0);
} else {
//非强制关闭发送Ctr+C信号
// 非强制关闭发送Ctr+C信号 [AUTO-TRANSLATED:fe9bf53f]
// Non-forced closure, send Ctrl+C signal.
signalCtrl(pid, CTRL_C_EVENT);
}
#else
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
//进程可能已经退出了
// 进程可能已经退出了 [AUTO-TRANSLATED:682a8b61]
// The process may have already exited.
WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
return;
}
#endif // _WIN32
if (force) {
//发送SIGKILL信号后阻塞等待退出
// 发送SIGKILL信号后阻塞等待退出 [AUTO-TRANSLATED:4fc03dae]
// After sending the SIGKILL signal, block and wait for exit.
s_wait(pid, handle, nullptr, true);
DebugL << "force kill " << pid << " success!";
return;
}
//发送SIGTERM信号后2秒后检查子进程是否已经退出
// 发送SIGTERM信号后2秒后检查子进程是否已经退出 [AUTO-TRANSLATED:b9878b28]
// After sending the SIGTERM signal, check if the child process has exited after 2 seconds.
EventPollerPool::Instance().getPoller()->doDelayTask(max_delay, [pid, handle]() {
if (!s_wait(pid, handle, nullptr, false)) {
//进程已经退出了
// 进程已经退出了 [AUTO-TRANSLATED:ad02bb63]
// The process has exited.
return 0;
}
//进程还在运行
// 进程还在运行 [AUTO-TRANSLATED:b1aa9ba4]
// The process is still running.
WarnL << "process still working,force kill it:" << pid;
s_kill(pid, handle, 0, true);
return 0;

View File

@@ -134,17 +134,20 @@ void System::startDaemon(bool &kill_parent_if_failed) {
pid = fork();
if (pid == -1) {
WarnL << "fork失败:" << get_uv_errmsg();
//休眠1秒再试
// 休眠1秒再试 [AUTO-TRANSLATED:00e5d7bf]
// Sleep for 1 second and try again
sleep(1);
continue;
}
if (pid == 0) {
//子进程
// 子进程 [AUTO-TRANSLATED:3f793797]
// Child process
return;
}
//父进程,监视子进程是否退出
// 父进程,监视子进程是否退出 [AUTO-TRANSLATED:0e13a34d]
// Parent process, monitor whether the child process exits
DebugL << "启动子进程:" << pid;
signal(SIGINT, [](int) {
WarnL << "收到主动退出信号,关闭父进程与子进程";
@@ -162,9 +165,11 @@ void System::startDaemon(bool &kill_parent_if_failed) {
int status = 0;
if (waitpid(pid, &status, 0) >= 0) {
WarnL << "子进程退出";
//休眠3秒再启动子进程
// 休眠3秒再启动子进程 [AUTO-TRANSLATED:608448bd]
// Sleep for 3 seconds and then start the child process
sleep(3);
//重启子进程,如果子进程重启失败,那么不应该杀掉守护进程,这样守护进程可以一直尝试重启子进程
// 重启子进程,如果子进程重启失败,那么不应该杀掉守护进程,这样守护进程可以一直尝试重启子进程 [AUTO-TRANSLATED:0a336b0a]
// Restart the child process. If the child process fails to restart, the daemon process should not be killed. This allows the daemon process to continuously attempt to restart the child process.
kill_parent_if_failed = false;
break;
}
@@ -204,7 +209,8 @@ void System::systemSetup(){
#ifndef ANDROID
signal(SIGSEGV, sig_crash);
signal(SIGABRT, sig_crash);
//忽略挂起信号
// 忽略挂起信号 [AUTO-TRANSLATED:73e71e54]
// Ignore the hang up signal
signal(SIGHUP, SIG_IGN);
#endif// ANDROID
#endif//!defined(_WIN32)

View File

@@ -80,14 +80,17 @@ void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr&
memcpy(buf->get()->data[0] + buf->get()->linesize[0] * (i + p->posY) + p->posX,
_tmp->get()->data[0] + _tmp->get()->linesize[0] * i, _tmp->get()->width);
}
// 确保height为奇数时也能正确的复制到最后一行uv数据
// 确保height为奇数时也能正确的复制到最后一行uv数据 [AUTO-TRANSLATED:69895ea5]
// Ensure that the uv data can be copied to the last line correctly when height is odd
for (int i = 0; i < (p->height + 1) / 2; i++) {
// U平面
// U平面 [AUTO-TRANSLATED:8b73dc2d]
// U plane
memcpy(buf->get()->data[1] + buf->get()->linesize[1] * (i + p->posY / 2) +
p->posX / 2,
_tmp->get()->data[1] + _tmp->get()->linesize[1] * i, _tmp->get()->width / 2);
// V平面
// V平面 [AUTO-TRANSLATED:8fa72cc7]
// V plane
memcpy(buf->get()->data[2] + buf->get()->linesize[2] * (i + p->posY / 2) +
p->posX / 2,
_tmp->get()->data[2] + _tmp->get()->linesize[2] * i, _tmp->get()->width / 2);
@@ -95,7 +98,8 @@ void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr&
break;
}
case AV_PIX_FMT_NV12: {
// TODO: 待实现
// TODO: 待实现 [AUTO-TRANSLATED:247ec1df]
// TODO: To be implemented
break;
}
@@ -110,7 +114,8 @@ void StackPlayer::addChannel(const std::weak_ptr<Channel>& chn) {
void StackPlayer::play() {
auto url = _url;
// 创建拉流 解码对象
// 创建拉流 解码对象 [AUTO-TRANSLATED:9267c5dc]
// Create a pull stream decoding object
_player = std::make_shared<mediakit::MediaPlayer>();
std::weak_ptr<mediakit::MediaPlayer> weakPlayer = _player;
@@ -127,7 +132,8 @@ void StackPlayer::play() {
if (!self) { return; }
if (!ex) {
// 取消定时器
// 取消定时器 [AUTO-TRANSLATED:41ff7c9a]
// Cancel the timer
self->_timer.reset();
self->_failedCount = 0;
@@ -141,7 +147,8 @@ void StackPlayer::play() {
// auto audioTrack = std::dynamic_pointer_cast<mediakit::AudioTrack>(strongPlayer->getTrack(mediakit::TrackAudio, false));
if (videoTrack) {
// TODO:添加使用显卡还是cpu解码的判断逻辑
// TODO:添加使用显卡还是cpu解码的判断逻辑 [AUTO-TRANSLATED:44bef37a]
// TODO: Add logic to determine whether to use GPU or CPU decoding
auto decoder = std::make_shared<mediakit::FFmpegDecoder>(
videoTrack, 0, std::vector<std::string>{"h264", "hevc"});
@@ -227,7 +234,8 @@ VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelForm
info.iBitRate = _bitRate;
_dev->initVideo(info);
// dev->initAudio(); //TODO:音频
// dev->initAudio(); //TODO:音频 [AUTO-TRANSLATED:adc5658b]
// dev->initAudio(); //TODO: Audio
_dev->addTrackCompleted();
_isExit = false;
@@ -276,7 +284,8 @@ void VideoStack::start() {
}
void VideoStack::initBgColor() {
// 填充底色
// 填充底色 [AUTO-TRANSLATED:ee9bbd46]
// Fill the background color
auto R = 20;
auto G = 20;
auto B = 20;
@@ -402,11 +411,13 @@ Params VideoStackManager::parseParams(const Json::Value& json, std::string& id,
float gapv = json["gapv"].asFloat();// 垂直间距
float gaph = json["gaph"].asFloat();// 水平间距
// 单个间距
// 单个间距 [AUTO-TRANSLATED:e1b9b5b6]
// Single spacing
int gaphPix = static_cast<int>(round(width * gaph));
int gapvPix = static_cast<int>(round(height * gapv));
// 根据间距计算格子宽高
// 根据间距计算格子宽高 [AUTO-TRANSLATED:b9972498]
// Calculate the width and height of the grid according to the spacing
int gridWidth = cols > 1 ? (width - gaphPix * (cols - 1)) / cols : width;
int gridHeight = rows > 1 ? (height - gapvPix * (rows - 1)) / rows : height;
@@ -427,7 +438,8 @@ Params VideoStackManager::parseParams(const Json::Value& json, std::string& id,
}
}
// 判断是否需要合并格子 (焦点屏)
// 判断是否需要合并格子 (焦点屏) [AUTO-TRANSLATED:bfa14430]
// Determine whether to merge grids (focus screen)
if (json.isMember("span") && json["span"].isArray() && json["span"].size() > 0) {
for (const auto& subArray : json["span"]) {
if (!subArray.isArray() || subArray.size() != 2) {

View File

@@ -98,7 +98,8 @@ private:
std::string _url;
mediakit::MediaPlayer::Ptr _player;
// 用于断线重连
// 用于断线重连 [AUTO-TRANSLATED:18fd242a]
// Used for disconnection and reconnection
toolkit::Timer::Ptr _timer;
int _failedCount = 0;
@@ -145,13 +146,16 @@ private:
class VideoStackManager {
public:
// 创建拼接流
// 创建拼接流 [AUTO-TRANSLATED:ebb3a8ec]
// Create a concatenated stream
int startVideoStack(const Json::Value& json);
// 停止拼接流
// 停止拼接流 [AUTO-TRANSLATED:a46f341f]
// Stop the concatenated stream
int stopVideoStack(const std::string& id);
// 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容)
// 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容) [AUTO-TRANSLATED:f9b59b6b]
// You can modify the configuration of the concatenated stream (to switch the content of the concatenated screen) without stopping the stream
int resetVideoStack(const Json::Value& json);
public:

View File

@@ -90,7 +90,8 @@ static onceToken token([]() {
}//namespace API
using HttpApi = function<void(const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender)>;
//http api列表
// http api列表 [AUTO-TRANSLATED:a05e9d9d]
// http api list
static map<string, HttpApi, StrCaseCompare> s_map_api;
static void responseApi(const Json::Value &res, const HttpSession::HttpResponseInvoker &invoker){
@@ -118,7 +119,8 @@ static HttpApi toApi(const function<void(API_ARGS_MAP_ASYNC)> &cb) {
Json::Value val;
val["code"] = API::Success;
//参数解析成map
// 参数解析成map [AUTO-TRANSLATED:20e11ff3]
// Parse parameters into a map
auto args = getAllArgs(parser);
cb(sender, headerOut, ArgsMap(parser, args), val, invoker);
};
@@ -143,7 +145,8 @@ static HttpApi toApi(const function<void(API_ARGS_JSON_ASYNC)> &cb) {
if (parser["Content-Type"].find("application/json") == string::npos) {
throw InvalidArgsException("该接口只支持json格式的请求");
}
//参数解析成json对象然后处理
// 参数解析成json对象然后处理 [AUTO-TRANSLATED:6f23397b]
// Parse parameters into a JSON object and then process
Json::Value args;
Json::Reader reader;
reader.parse(parser.content(), args);
@@ -203,7 +206,8 @@ void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYN
s_map_api.emplace(api_path, toApi(func));
}
//获取HTTP请求中url参数、content参数
// 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1]
// Get URL parameters and content parameters from the HTTP request
static ApiArgsType getAllArgs(const Parser &parser) {
ApiArgsType allArgs;
if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) {
@@ -245,21 +249,25 @@ static void *web_api_tag = nullptr;
static inline void addHttpListener(){
GET_CONFIG(bool, api_debug, API::kApiDebug);
//注册监听kBroadcastHttpRequest事件
// 注册监听kBroadcastHttpRequest事件 [AUTO-TRANSLATED:4af22c90]
// Register to listen for the kBroadcastHttpRequest event
NoticeCenter::Instance().addListener(&web_api_tag, Broadcast::kBroadcastHttpRequest, [](BroadcastHttpRequestArgs) {
auto it = s_map_api.find(parser.url());
if (it == s_map_api.end()) {
return;
}
//该api已被消费
// 该api已被消费 [AUTO-TRANSLATED:db0872fc]
// This API has been consumed
consumed = true;
if(api_debug){
auto newInvoker = [invoker, parser](int code, const HttpSession::KeyValue &headerOut, const HttpBody::Ptr &body) {
//body默认为空
// body默认为空 [AUTO-TRANSLATED:4fd4ecc8]
// The body is empty by default
ssize_t size = 0;
if (body && body->remainSize()) {
//有body获取body大小
// 有body获取body大小 [AUTO-TRANSLATED:ab1c417d]
// If there is a body, get the body size
size = body->remainSize();
}
@@ -362,17 +370,21 @@ public:
}
};
//拉流代理器列表
// 拉流代理器列表 [AUTO-TRANSLATED:6dcfb11f]
// Pull stream proxy list
static ServiceController<PlayerProxy> s_player_proxy;
//推流代理器列表
// 推流代理器列表 [AUTO-TRANSLATED:539a1bcf]
// Push stream proxy list
static ServiceController<PusherProxy> s_pusher_proxy;
//FFmpeg拉流代理器列表
// FFmpeg拉流代理器列表 [AUTO-TRANSLATED:4bdedf10]
// FFmpeg pull stream proxy list
static ServiceController<FFmpegSource> s_ffmpeg_src;
#if defined(ENABLE_RTPPROXY)
//rtp服务器列表
// rtp服务器列表 [AUTO-TRANSLATED:2e362a8c]
// RTP server list
static ServiceController<RtpServer> s_rtp_server;
#endif
@@ -418,7 +430,8 @@ Value makeMediaSourceJson(MediaSource &media){
item["originSock"] = Json::nullValue;
}
//getLossRate有线程安全问题使用getMediaInfo接口才能获取丢包率getMediaList接口将忽略丢包率
// getLossRate有线程安全问题使用getMediaInfo接口才能获取丢包率getMediaList接口将忽略丢包率 [AUTO-TRANSLATED:b2e927c6]
// getLossRate has thread safety issues; use the getMediaInfo interface to get the packet loss rate; the getMediaList interface will ignore the packet loss rate
auto current_thread = false;
try { current_thread = media.getOwnerPoller()->isCurrentThread();} catch (...) {}
float last_loss = -1;
@@ -430,7 +443,8 @@ Value makeMediaSourceJson(MediaSource &media){
obj["ready"] = track->ready();
obj["codec_type"] = codec_type;
if (current_thread) {
//rtp推流只有一个统计器但是可能有多个track如果短时间多次获取间隔丢包率第二次会获取为-1
// rtp推流只有一个统计器但是可能有多个track如果短时间多次获取间隔丢包率第二次会获取为-1 [AUTO-TRANSLATED:5bfbc951]
// RTP push stream has only one statistics, but may have multiple tracks. If you get the interval packet loss rate multiple times in a short time, the second time will get -1
auto loss = media.getLossRate(codec_type);
if (loss == -1) {
loss = last_loss;
@@ -477,7 +491,8 @@ Value makeMediaSourceJson(MediaSource &media){
uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) {
auto key = tuple.shortUrl();
if (s_rtp_server.find(key)) {
//为了防止RtpProcess所有权限混乱的问题不允许重复添加相同的key
// 为了防止RtpProcess所有权限混乱的问题不允许重复添加相同的key [AUTO-TRANSLATED:06c7b14c]
// To prevent the problem of all permissions being messed up in RtpProcess, duplicate keys are not allowed to be added
return 0;
}
@@ -485,11 +500,13 @@ uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, i
server->start(local_port, local_ip.c_str(), tuple, (RtpServer::TcpMode)tcp_mode, re_use_port, ssrc, only_track, multiplex);
});
server->setOnDetach([key](const SockException &ex) {
//设置rtp超时移除事件
// 设置rtp超时移除事件 [AUTO-TRANSLATED:98d42cf3]
// Set RTP timeout removal event
s_rtp_server.erase(key);
});
//回复json
// 回复json [AUTO-TRANSLATED:0c443c6a]
// Reply JSON
return server->getPort();
}
@@ -543,7 +560,8 @@ void getStatisticJson(const function<void(Value &val)> &cb) {
for (auto &val : *thread_mem_info) {
(*obj)["threadMem"].append(val);
}
//触发回调
// 触发回调 [AUTO-TRANSLATED:08ea452d]
// Trigger callback
cb(*obj);
});
@@ -584,27 +602,33 @@ void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count,
const function<void(const SockException &ex, const string &key)> &cb) {
auto key = tuple.shortUrl();
if (s_player_proxy.find(key)) {
//已经在拉流了
// 已经在拉流了 [AUTO-TRANSLATED:e06c57d7]
// Already pulling stream
cb(SockException(Err_other, "This stream already exists"), key);
return;
}
//添加拉流代理
// 添加拉流代理 [AUTO-TRANSLATED:aa516f44]
// Add pull stream proxy
auto player = s_player_proxy.make(key, tuple, option, retry_count);
// 先透传拷贝参数
// 先透传拷贝参数 [AUTO-TRANSLATED:22b5605e]
// First pass-through copy parameters
for (auto &pr : args) {
(*player)[pr.first] = pr.second;
}
//指定RTP over TCP(播放rtsp时有效)
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
// Specify RTP over TCP (effective when playing RTSP)
(*player)[Client::kRtpType] = rtp_type;
if (timeout_sec > 0.1f) {
//播放握手超时时间
// 播放握手超时时间 [AUTO-TRANSLATED:5a29ae1f]
// Play handshake timeout
(*player)[Client::kTimeoutMS] = timeout_sec * 1000;
}
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
// 开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试 [AUTO-TRANSLATED:ac8499e5]
// Start playing. If playback fails or is stopped, it will automatically retry several times, by default it will retry indefinitely
player->setPlayCallbackOnce([cb, key](const SockException &ex) {
if (ex) {
s_player_proxy.erase(key);
@@ -612,7 +636,8 @@ void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count,
cb(ex, key);
});
//被主动关闭拉流
// 被主动关闭拉流 [AUTO-TRANSLATED:41a19476]
// The pull stream was actively closed
player->setOnClose([key](const SockException &ex) {
s_player_proxy.erase(key);
});
@@ -636,23 +661,28 @@ void addStreamPusherProxy(const string &schema,
return;
}
if (s_pusher_proxy.find(key)) {
//已经在推流了
// 已经在推流了 [AUTO-TRANSLATED:81fcd202]
// Already pushing stream
cb(SockException(Err_success), key);
return;
}
//添加推流代理
// 添加推流代理 [AUTO-TRANSLATED:f9dbc76d]
// Add push stream proxy
auto pusher = s_pusher_proxy.make(key, src, retry_count);
//指定RTP over TCP(播放rtsp时有效)
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
// Specify RTP over TCP (effective when playing RTSP)
pusher->emplace(Client::kRtpType, rtp_type);
if (timeout_sec > 0.1f) {
//推流握手超时时间
// 推流握手超时时间 [AUTO-TRANSLATED:00762fc1]
// Push stream handshake timeout
pusher->emplace(Client::kTimeoutMS, timeout_sec * 1000);
}
//开始推流,如果推流失败或者推流中止,将会自动重试若干次,默认一直重试
// 开始推流,如果推流失败或者推流中止,将会自动重试若干次,默认一直重试 [AUTO-TRANSLATED:c8b95088]
// Start pushing stream. If the push stream fails or is stopped, it will automatically retry several times, by default it will retry indefinitely
pusher->setPushCallbackOnce([cb, key, url](const SockException &ex) {
if (ex) {
WarnL << "Push " << url << " failed, key: " << key << ", err: " << ex;
@@ -661,7 +691,8 @@ void addStreamPusherProxy(const string &schema,
cb(ex, key);
});
//被主动关闭推流
// 被主动关闭推流 [AUTO-TRANSLATED:bf216f82]
// Stream closed actively
pusher->setOnClose([key, url](const SockException &ex) {
WarnL << "Push " << url << " failed, key: " << key << ", err: " << ex;
s_pusher_proxy.erase(key);
@@ -674,13 +705,20 @@ void addStreamPusherProxy(const string &schema,
* 安装api接口
* 所有api都支持GET和POST两种方式
* POST方式参数支持application/json和application/x-www-form-urlencoded方式
* Install api interface
* All apis support GET and POST methods
* POST method parameters support application/json and application/x-www-form-urlencoded methods
* [AUTO-TRANSLATED:62e68c43]
*/
void installWebApi() {
addHttpListener();
GET_CONFIG(string,api_secret,API::kSecret);
//获取线程负载
//测试url http://127.0.0.1/index/api/getThreadsLoad
// 获取线程负载 [AUTO-TRANSLATED:3b0ece5c]
// Get thread load
// 测试url http://127.0.0.1/index/api/getThreadsLoad [AUTO-TRANSLATED:de1c93e7]
// Test url http://127.0.0.1/index/api/getThreadsLoad
api_regist("/index/api/getThreadsLoad", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
EventPollerPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
@@ -698,8 +736,10 @@ void installWebApi() {
});
});
//获取后台工作线程负载
//测试url http://127.0.0.1/index/api/getWorkThreadsLoad
// 获取后台工作线程负载 [AUTO-TRANSLATED:6166e265]
// Get background worker thread load
// 测试url http://127.0.0.1/index/api/getWorkThreadsLoad [AUTO-TRANSLATED:209a8bc1]
// Test url http://127.0.0.1/index/api/getWorkThreadsLoad
api_regist("/index/api/getWorkThreadsLoad", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
WorkThreadPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
@@ -717,8 +757,10 @@ void installWebApi() {
});
});
//获取服务器配置
//测试url http://127.0.0.1/index/api/getServerConfig
// 获取服务器配置 [AUTO-TRANSLATED:7dd2f3da]
// Get server configuration
// 测试url http://127.0.0.1/index/api/getServerConfig [AUTO-TRANSLATED:59cd0d71]
// Test url http://127.0.0.1/index/api/getServerConfig
api_regist("/index/api/getServerConfig",[](API_ARGS_MAP){
CHECK_SECRET();
Value obj;
@@ -728,9 +770,12 @@ void installWebApi() {
val["data"].append(obj);
});
//设置服务器配置
//测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
//你也可以通过http post方式传参可以通过application/x-www-form-urlencoded或application/json方式传参
// 设置服务器配置 [AUTO-TRANSLATED:3de7bd37]
// Set server configuration
// 测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0 [AUTO-TRANSLATED:9471d218]
// Test url (e.g. disable http api debugging) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
// 你也可以通过http post方式传参可以通过application/x-www-form-urlencoded或application/json方式传参 [AUTO-TRANSLATED:d493a7c0]
// You can also pass parameters through http post method, you can pass parameters through application/x-www-form-urlencoded or application/json methods
api_regist("/index/api/setServerConfig",[](API_ARGS_MAP){
CHECK_SECRET();
auto &ini = mINI::Instance();
@@ -738,12 +783,15 @@ void installWebApi() {
for (auto &pr : allArgs.args) {
if (ini.find(pr.first) == ini.end()) {
#if 1
//没有这个key
// 没有这个key [AUTO-TRANSLATED:d6855e02]
// This key does not exist
continue;
#else
// 新增配置选项,为了动态添加多个ffmpeg cmd 模板
// 新增配置选项,为了动态添加多个ffmpeg cmd 模板 [AUTO-TRANSLATED:0f977fcd]
// Add configuration options to dynamically add multiple ffmpeg cmd templates
ini[pr.first] = pr.second;
// 防止changed变化
// 防止changed变化 [AUTO-TRANSLATED:f8ad7e59]
// Prevent changed changes
continue;
#endif
}
@@ -755,7 +803,8 @@ void installWebApi() {
continue;
}
ini[pr.first] = pr.second;
//替换成功
// 替换成功 [AUTO-TRANSLATED:b5d4fec1]
// Replacement successful
++changed;
}
if (changed > 0) {
@@ -773,28 +822,36 @@ void installWebApi() {
}
};
//获取服务器api列表
//测试url http://127.0.0.1/index/api/getApiList
// 获取服务器api列表 [AUTO-TRANSLATED:e4c0dd9d]
// Get server api list
// 测试url http://127.0.0.1/index/api/getApiList [AUTO-TRANSLATED:df09e368]
// Test url http://127.0.0.1/index/api/getApiList
api_regist("/index/api/getApiList",[](API_ARGS_MAP){
s_get_api_list(API_ARGS_VALUE);
});
//获取服务器api列表
//测试url http://127.0.0.1/index/
// 获取服务器api列表 [AUTO-TRANSLATED:e4c0dd9d]
// Get server api list
// 测试url http://127.0.0.1/index/ [AUTO-TRANSLATED:76934dd3]
// Test url http://127.0.0.1/index/
api_regist("/index/",[](API_ARGS_MAP){
s_get_api_list(API_ARGS_VALUE);
});
#if !defined(_WIN32)
//重启服务器,只有Daemon方式才能重启否则是直接关闭
//测试url http://127.0.0.1/index/api/restartServer
// 重启服务器,只有Daemon方式才能重启否则是直接关闭 [AUTO-TRANSLATED:9d8a1c32]
// Restart server, only Daemon mode can restart, otherwise it will be closed directly!
// 测试url http://127.0.0.1/index/api/restartServer [AUTO-TRANSLATED:8beaaa8a]
// Test url http://127.0.0.1/index/api/restartServer
api_regist("/index/api/restartServer",[](API_ARGS_MAP){
CHECK_SECRET();
EventPollerPool::Instance().getPoller()->doDelayTask(1000,[](){
//尝试正常退出
// 尝试正常退出 [AUTO-TRANSLATED:93828d0f]
// Try to exit normally
::kill(getpid(), SIGINT);
//3秒后强制退出
// 3秒后强制退出 [AUTO-TRANSLATED:fdc82920]
// Force exit after 3 seconds
EventPollerPool::Instance().getPoller()->doDelayTask(3000,[](){
exit(0);
return 0;
@@ -805,10 +862,12 @@ void installWebApi() {
val["msg"] = "MediaServer will reboot in on 1 second";
});
#else
//增加Windows下的重启代码
// 增加Windows下的重启代码 [AUTO-TRANSLATED:dcba12d5]
// Add restart code for Windows
api_regist("/index/api/restartServer", [](API_ARGS_MAP) {
CHECK_SECRET();
//创建重启批处理脚本文件
// 创建重启批处理脚本文件 [AUTO-TRANSLATED:cc18c259]
// Create a restart batch script file
FILE *pf;
errno_t err = ::_wfopen_s(&pf, L"RestartServer.cmd", L"w"); //“w”如果该文件存在其内容将被覆盖
if (err == 0) {
@@ -824,7 +883,8 @@ void installWebApi() {
strcat(exeName, ext);
fprintf(pf, "@echo off\ntaskkill /f /im %s\nstart \"\" \"%s\"\ndel %%0", exeName, szExeName);
fclose(pf);
// 1秒后执行创建的批处理脚本
// 1秒后执行创建的批处理脚本 [AUTO-TRANSLATED:596dbca9]
// Execute the created batch script after 1 second
EventPollerPool::Instance().getPoller()->doDelayTask(1000, []() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
@@ -859,27 +919,35 @@ void installWebApi() {
});
#endif//#if !defined(_WIN32)
//获取流列表,可选筛选参数
//测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList
//测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__
//测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp
// 获取流列表,可选筛选参数 [AUTO-TRANSLATED:68ffc6b6]
// Get stream list, optional filtering parameters
// 测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList [AUTO-TRANSLATED:434652ea]
// Test url0 (get all streams) http://127.0.0.1/index/api/getMediaList
// 测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__ [AUTO-TRANSLATED:5d9bd1ee]
// Test url1 (get streams with virtual host "__defaultVost__") http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__
// 测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp [AUTO-TRANSLATED:21c2c15d]
// Test url2 (get rtsp type streams) http://127.0.0.1/index/api/getMediaList?schema=rtsp
api_regist("/index/api/getMediaList",[](API_ARGS_MAP){
CHECK_SECRET();
//获取所有MediaSource列表
// 获取所有MediaSource列表 [AUTO-TRANSLATED:7bf16dc2]
// Get all MediaSource lists
MediaSource::for_each_media([&](const MediaSource::Ptr &media) {
val["data"].append(makeMediaSourceJson(*media));
}, allArgs["schema"], allArgs["vhost"], allArgs["app"], allArgs["stream"]);
});
//测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
// 测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:126a75e8]
// Test url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist("/index/api/isMediaOnline",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"]));
});
//获取媒体流播放器列表
//测试url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
// 获取媒体流播放器列表 [AUTO-TRANSLATED:bcadf31c]
// Get media stream player list
// 测试url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:2aab7522]
// Test url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist("/index/api/getMediaPlayerList",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("schema", "vhost", "app", "stream");
@@ -922,7 +990,8 @@ void installWebApi() {
src->broadcastMessage(any);
});
//测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
// 测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:9402e811]
// Test url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist("/index/api/getMediaInfo",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
@@ -937,12 +1006,15 @@ void installWebApi() {
});
});
//主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
// 主动关断流,包括关断拉流、推流 [AUTO-TRANSLATED:80506955]
// Actively close the stream, including closing the pull stream and push stream
// 测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 [AUTO-TRANSLATED:c3831592]
// Test url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
api_regist("/index/api/close_stream",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
//踢掉推流器
// 踢掉推流器 [AUTO-TRANSLATED:61e39b14]
// Kick out the pusher
auto src = MediaSource::find(allArgs["schema"],
allArgs["vhost"],
allArgs["app"],
@@ -961,11 +1033,14 @@ void installWebApi() {
});
});
//批量主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
// 批量主动关断流,包括关断拉流、推流 [AUTO-TRANSLATED:5d180cd8]
// Batch actively close the stream, including closing the pull stream and push stream
// 测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 [AUTO-TRANSLATED:786933db]
// Test url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
api_regist("/index/api/close_streams",[](API_ARGS_MAP){
CHECK_SECRET();
//筛选命中个数
// 筛选命中个数 [AUTO-TRANSLATED:6db1e8c7]
// Filter hit count
int count_hit = 0;
int count_closed = 0;
list<MediaSource::Ptr> media_list;
@@ -984,9 +1059,12 @@ void installWebApi() {
val["count_closed"] = count_closed;
});
//获取所有Session列表信息
//可以根据本地端口和远端ip来筛选
//测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
// 获取所有Session列表信息 [AUTO-TRANSLATED:e785052d]
// Get all Session list information
// 可以根据本地端口和远端ip来筛选 [AUTO-TRANSLATED:4d4c9d61]
// You can filter by local port and remote ip
// 测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935 [AUTO-TRANSLATED:ef845193]
// Test url (filter tcp session under a certain port) http://127.0.0.1/index/api/getAllSession?local_port=1935
api_regist("/index/api/getAllSession",[](API_ARGS_MAP){
CHECK_SECRET();
Value jsession;
@@ -1007,12 +1085,15 @@ void installWebApi() {
});
});
//断开tcp连接比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_session?id=123456
// 断开tcp连接比如说可以断开rtsp、rtmp播放器等 [AUTO-TRANSLATED:9147ffec]
// Disconnect the tcp connection, for example, you can disconnect the rtsp, rtmp player, etc.
// 测试url http://127.0.0.1/index/api/kick_session?id=123456 [AUTO-TRANSLATED:c2880cb5]
// Test url http://127.0.0.1/index/api/kick_session?id=123456
api_regist("/index/api/kick_session",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("id");
//踢掉tcp会话
// 踢掉tcp会话 [AUTO-TRANSLATED:f6f318bd]
// Kick out the tcp session
auto session = SessionMap::Instance().get(allArgs["id"]);
if(!session){
throw ApiRetException("can not find the target",API::OtherFailed);
@@ -1021,8 +1102,10 @@ void installWebApi() {
});
//批量断开tcp连接比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935
// 批量断开tcp连接比如说可以断开rtsp、rtmp播放器等 [AUTO-TRANSLATED:fef59eb8]
// Batch disconnect tcp connections, for example, you can disconnect rtsp, rtmp players, etc.
// 测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935 [AUTO-TRANSLATED:5891b482]
// Test url http://127.0.0.1/index/api/kick_sessions?local_port=1935
api_regist("/index/api/kick_sessions", [](API_ARGS_MAP) {
CHECK_SECRET();
uint16_t local_port = allArgs["local_port"].as<uint16_t>();
@@ -1038,7 +1121,8 @@ void installWebApi() {
return;
}
if (session->getIdentifier() == sender.getIdentifier()) {
// 忽略本http链接
// 忽略本http链接 [AUTO-TRANSLATED:9fb4bf76]
// Ignore this http link
return;
}
session_list.emplace_back(session);
@@ -1051,8 +1135,10 @@ void installWebApi() {
val["count_hit"] = (Json::UInt64)count_hit;
});
//动态添加rtsp/rtmp推流代理
//测试url http://127.0.0.1/index/api/addStreamPusherProxy?schema=rtmp&vhost=__defaultVhost__&app=proxy&stream=0&dst_url=rtmp://127.0.0.1/live/obs
// 动态添加rtsp/rtmp推流代理 [AUTO-TRANSLATED:2eb09bc9]
// Dynamically add rtsp/rtmp push stream proxy
// 测试url http://127.0.0.1/index/api/addStreamPusherProxy?schema=rtmp&vhost=__defaultVhost__&app=proxy&stream=0&dst_url=rtmp://127.0.0.1/live/obs [AUTO-TRANSLATED:25d7d4b0]
// Test url http://127.0.0.1/index/api/addStreamPusherProxy?schema=rtmp&vhost=__defaultVhost__&app=proxy&stream=0&dst_url=rtmp://127.0.0.1/live/obs
api_regist("/index/api/addStreamPusherProxy", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
CHECK_ARGS("schema", "vhost", "app", "stream", "dst_url");
@@ -1078,16 +1164,20 @@ void installWebApi() {
});
});
//关闭推流代理
//测试url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0
// 关闭推流代理 [AUTO-TRANSLATED:91602b75]
// Close the push stream proxy
// 测试url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0 [AUTO-TRANSLATED:2671206c]
// Test url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0
api_regist("/index/api/delStreamPusherProxy", [](API_ARGS_MAP) {
CHECK_SECRET();
CHECK_ARGS("key");
val["data"]["flag"] = s_pusher_proxy.erase(allArgs["key"]) == 1;
});
//动态添加rtsp/rtmp拉流代理
//测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
// 动态添加rtsp/rtmp拉流代理 [AUTO-TRANSLATED:2616537c]
// Dynamically add rtsp/rtmp pull stream proxy
// 测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs [AUTO-TRANSLATED:71ddce15]
// Test url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
api_regist("/index/api/addStreamProxy",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("vhost","app","stream","url");
@@ -1123,8 +1213,10 @@ void installWebApi() {
});
});
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
// 关闭拉流代理 [AUTO-TRANSLATED:5204f128]
// Close the pull stream proxy
// 测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0 [AUTO-TRANSLATED:2b0903ef]
// Test url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
api_regist("/index/api/delStreamProxy",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("key");
@@ -1140,7 +1232,8 @@ void installWebApi() {
const function<void(const SockException &ex, const string &key)> &cb) {
auto key = MD5(dst_url).hexdigest();
if (s_ffmpeg_src.find(key)) {
//已经在拉流了
// 已经在拉流了 [AUTO-TRANSLATED:e06c57d7]
// Already pulling
cb(SockException(Err_success), key);
return;
}
@@ -1159,8 +1252,10 @@ void installWebApi() {
});
};
//动态添加rtsp/rtmp拉流代理
//测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000
// 动态添加rtsp/rtmp拉流代理 [AUTO-TRANSLATED:2616537c]
// Dynamically add rtsp/rtmp pull stream proxy
// 测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000 [AUTO-TRANSLATED:501cdd89]
// // Test url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000
api_regist("/index/api/addFFmpegSource",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("src_url","dst_url","timeout_ms");
@@ -1182,16 +1277,20 @@ void installWebApi() {
});
});
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
// 关闭拉流代理 [AUTO-TRANSLATED:5204f128]
// Close the pull stream proxy
// 测试url http://127.0.0.1/index/api/delFFmepgSource?key=key [AUTO-TRANSLATED:ed6fa147]
// Test url http://127.0.0.1/index/api/delFFmepgSource?key=key
api_regist("/index/api/delFFmpegSource",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("key");
val["data"]["flag"] = s_ffmpeg_src.erase(allArgs["key"]) == 1;
});
//新增http api下载可执行程序文件接口
//测试url http://127.0.0.1/index/api/downloadBin
// 新增http api下载可执行程序文件接口 [AUTO-TRANSLATED:d6e44e84]
// Add a new http api to download executable files
// 测试url http://127.0.0.1/index/api/downloadBin [AUTO-TRANSLATED:9525e834]
// Test url http://127.0.0.1/index/api/downloadBin
api_regist("/index/api/downloadBin",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
invoker.responseFile(allArgs.parser.getHeader(), StrCaseMap(), exePath());
@@ -1234,12 +1333,14 @@ void installWebApi() {
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto tcp_mode = allArgs["tcp_mode"].as<int>();
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
//兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
// 兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数 [AUTO-TRANSLATED:3b6a5ab5]
// Compatible with old version requests, the new version removes the enable_tcp parameter and adds the tcp_mode parameter
tcp_mode = 1;
}
auto only_track = allArgs["only_track"].as<int>();
if (allArgs["only_audio"].as<bool>()) {
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数 [AUTO-TRANSLATED:a7a40942]
// Compatible with old version requests, the new version removes the only_audio parameter and adds the only_track parameter
only_track = 1;
}
GET_CONFIG(std::string, local_ip, General::kListenIP)
@@ -1251,7 +1352,8 @@ void installWebApi() {
if (port == 0) {
throw InvalidArgsException("This stream already exists");
}
//回复json
// 回复json [AUTO-TRANSLATED:0c443c6a]
// Reply json
val["port"] = port;
});
@@ -1270,12 +1372,14 @@ void installWebApi() {
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto tcp_mode = allArgs["tcp_mode"].as<int>();
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
// 兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
// 兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数 [AUTO-TRANSLATED:b5f8f5df]
// Compatible with old version requests, the new version removes the enable_tcp parameter and adds the tcp_mode parameter
tcp_mode = 1;
}
auto only_track = allArgs["only_track"].as<int>();
if (allArgs["only_audio"].as<bool>()) {
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数 [AUTO-TRANSLATED:a7a40942]
// Compatible with old version requests, the new version removes the only_audio parameter and adds the only_track parameter
only_track = 1;
}
std::string local_ip = "::";
@@ -1287,7 +1391,8 @@ void installWebApi() {
if (port == 0) {
throw InvalidArgsException("This stream already exists");
}
// 回复json
// 回复json [AUTO-TRANSLATED:e80815cd]
// Reply json
val["port"] = port;
});
@@ -1384,7 +1489,8 @@ void installWebApi() {
}
auto type = allArgs["type"].empty() ? (int)MediaSourceEvent::SendRtpArgs::kRtpPS : allArgs["type"].as<int>();
if (!allArgs["use_ps"].empty()) {
// 兼容之前的use_ps参数
// 兼容之前的use_ps参数 [AUTO-TRANSLATED:0193f489]
// Compatible with the previous use_ps parameter
type = allArgs["use_ps"].as<int>();
}
MediaSourceEvent::SendRtpArgs args;
@@ -1404,7 +1510,8 @@ void installWebApi() {
args.udp_rtcp_timeout = allArgs["udp_rtcp_timeout"];
args.recv_stream_id = allArgs["recv_stream_id"];
args.close_delay_ms = allArgs["close_delay_ms"];
// 记录发送流的app和vhost
// 记录发送流的app和vhost [AUTO-TRANSLATED:ee1b41d5]
// Record the app and vhost of the sending stream
args.recv_stream_app = allArgs["app"];
args.recv_stream_vhost = allArgs["vhost"];
src->getOwnerPoller()->async([=]() mutable {
@@ -1467,7 +1574,8 @@ void installWebApi() {
}
src->getOwnerPoller()->async([=]() mutable {
// ssrc如果为空关闭全部
// ssrc如果为空关闭全部 [AUTO-TRANSLATED:e0955dab]
// If ssrc is empty, close all
if (!src->stopSendRtp(allArgs["ssrc"])) {
val["code"] = API::OtherFailed;
val["msg"] = "stopSendRtp failed";
@@ -1489,7 +1597,8 @@ void installWebApi() {
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
//只是暂停流的检查流媒体服务器做为流负载服务收流就转发RTSP/RTMP有自己暂停协议
// 只是暂停流的检查流媒体服务器做为流负载服务收流就转发RTSP/RTMP有自己暂停协议 [AUTO-TRANSLATED:dda6ee31]
// Only pause the stream check, the media server acts as a stream load balancing service, receiving the stream and forwarding it, RTSP/RTMP has its own pause protocol
auto src = MediaSource::find(vhost, app, allArgs["stream_id"]);
auto process = src ? src->getRtpProcess() : nullptr;
if (process) {
@@ -1521,7 +1630,8 @@ void installWebApi() {
#endif//ENABLE_RTPPROXY
// 开始录制hls或MP4
// 开始录制hls或MP4 [AUTO-TRANSLATED:0818775e]
// Start recording hls or MP4
api_regist("/index/api/startRecord",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("type","vhost","app","stream");
@@ -1540,7 +1650,8 @@ void installWebApi() {
});
});
//设置录像流播放速度
// 设置录像流播放速度 [AUTO-TRANSLATED:a8d82298]
// Set the playback speed of the recording stream
api_regist("/index/api/setRecordSpeed", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
CHECK_ARGS("schema", "vhost", "app", "stream", "speed");
@@ -1583,7 +1694,8 @@ void installWebApi() {
});
});
// 停止录制hls或MP4
// 停止录制hls或MP4 [AUTO-TRANSLATED:24d11a0c]
// Stop recording hls or MP4
api_regist("/index/api/stopRecord",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("type","vhost","app","stream");
@@ -1603,7 +1715,8 @@ void installWebApi() {
});
});
// 获取hls或MP4录制状态
// 获取hls或MP4录制状态 [AUTO-TRANSLATED:a08a2f1a]
// Get the recording status of hls or MP4
api_regist("/index/api/isRecording",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("type","vhost","app","stream");
@@ -1648,7 +1761,8 @@ void installWebApi() {
invoker(200, headerOut, val.toStyledString());
});
// 删除录像文件夹
// 删除录像文件夹 [AUTO-TRANSLATED:821aed07]
// Delete the recording folder
// http://127.0.0.1/index/api/deleteRecordDirectroy?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01-01
api_regist("/index/api/deleteRecordDirectory", [](API_ARGS_MAP) {
CHECK_SECRET();
@@ -1661,10 +1775,12 @@ void installWebApi() {
bool recording = false;
auto name = allArgs["name"];
if (!name.empty()) {
// 删除指定文件
// 删除指定文件 [AUTO-TRANSLATED:e8ee7bfa]
// Delete the specified file
record_path += name;
} else {
// 删除文件夹,先判断该流是否正在录制中
// 删除文件夹,先判断该流是否正在录制中 [AUTO-TRANSLATED:9f124786]
// Delete the folder, first check if the stream is being recorded
auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]);
if (src && src->isRecording(Recorder::type_mp4)) {
recording = true;
@@ -1689,7 +1805,8 @@ void installWebApi() {
File::deleteEmptyDir(record_path);
});
//获取录像文件夹列表或mp4文件列表
// 获取录像文件夹列表或mp4文件列表 [AUTO-TRANSLATED:f7e299bc]
// Get the list of recording folders or mp4 files
//http://127.0.0.1/index/api/getMP4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
api_regist("/index/api/getMP4RecordFile", [](API_ARGS_MAP){
CHECK_SECRET();
@@ -1698,25 +1815,29 @@ void installWebApi() {
auto record_path = Recorder::getRecordPath(Recorder::type_mp4, tuple, allArgs["customized_path"]);
auto period = allArgs["period"];
//判断是获取mp4文件列表还是获取文件夹列表
// 判断是获取mp4文件列表还是获取文件夹列表 [AUTO-TRANSLATED:b9c86d2f]
// Determine whether to get the mp4 file list or the folder list
bool search_mp4 = period.size() == sizeof("2020-02-01") - 1;
if (search_mp4) {
record_path = record_path + period + "/";
}
Json::Value paths(arrayValue);
//这是筛选日期,获取文件夹列表
// 这是筛选日期,获取文件夹列表 [AUTO-TRANSLATED:786fa49d]
// This is to filter the date and get the folder list
File::scanDir(record_path, [&](const string &path, bool isDir) {
auto pos = path.rfind('/');
if (pos != string::npos) {
string relative_path = path.substr(pos + 1);
if (search_mp4) {
if (!isDir) {
//我们只收集mp4文件对文件夹不感兴趣
// 我们只收集mp4文件对文件夹不感兴趣 [AUTO-TRANSLATED:254d9f25]
// We only collect mp4 files, we are not interested in folders
paths.append(relative_path);
}
} else if (isDir && relative_path.find(period) == 0) {
//匹配到对应日期的文件夹
// 匹配到对应日期的文件夹 [AUTO-TRANSLATED:cd3d10b9]
// Match the folder for the corresponding date
paths.append(relative_path);
}
}
@@ -1736,24 +1857,29 @@ void installWebApi() {
GET_CONFIG(string, defaultSnap, API::kDefaultSnap);
if (!File::fileSize(snap_path)) {
if (!err_msg.empty() && (!s_snap_success_once || defaultSnap.empty())) {
//重来没截图成功过或者默认截图图片为空那么直接返回FFmpeg错误日志
// 重来没截图成功过或者默认截图图片为空那么直接返回FFmpeg错误日志 [AUTO-TRANSLATED:5bde510f]
// If the screenshot has never been successful or the default screenshot image is empty, then directly return the FFmpeg error log
headerOut["Content-Type"] = HttpFileManager::getContentType(".txt");
invoker.responseFile(headerIn, headerOut, err_msg, false, false);
return;
}
//截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片
// 截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片 [AUTO-TRANSLATED:ffe4d807]
// If the screenshot has been successful once, then it is considered that the configuration is error-free, and when the screenshot fails, the preset default image is returned
const_cast<string &>(snap_path) = File::absolutePath("", defaultSnap);
headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data());
} else {
s_snap_success_once = true;
//之前生成的截图文件我们默认为jpeg格式
// 之前生成的截图文件我们默认为jpeg格式 [AUTO-TRANSLATED:5cc5c1ff]
// The previously generated screenshot file, we default to jpeg format
headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg");
}
//返回图片给http客户端
// 返回图片给http客户端 [AUTO-TRANSLATED:58a1f64e]
// Return image to http client
invoker.responseFile(headerIn, headerOut, snap_path);
};
//获取截图缓存或者实时截图
// 获取截图缓存或者实时截图 [AUTO-TRANSLATED:78e2fe1e]
// Get screenshot cache or real-time screenshot
//http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3
api_regist("/index/api/getSnap", [](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
@@ -1767,49 +1893,61 @@ void installWebApi() {
File::scanDir(scan_path, [&](const string &path, bool isDir) {
if (isDir || !end_with(path, ".jpeg")) {
//忽略文件夹或其他类型的文件
// 忽略文件夹或其他类型的文件 [AUTO-TRANSLATED:3ecffcae]
// Ignore folders or other types of files
return true;
}
//找到截图
// 找到截图 [AUTO-TRANSLATED:b784cfec]
// Find screenshot
auto tm = findSubString(path.data() + scan_path.size(), nullptr, ".jpeg");
if (atoll(tm.data()) + expire_sec < time(NULL)) {
//截图已经过期,改名,以便再次请求时,可以返回老截图
// 截图已经过期,改名,以便再次请求时,可以返回老截图 [AUTO-TRANSLATED:94fac79b]
// Screenshot has expired, rename it so that it can be returned when requested again
rename(path.data(), new_snap.data());
have_old_snap = true;
return true;
}
//截图存在,且未过期,那么返回之
// 截图存在,且未过期,那么返回之 [AUTO-TRANSLATED:6f53d3d1]
// Screenshot exists and has not expired, so return it
res_old_snap = true;
responseSnap(path, allArgs.parser.getHeader(), invoker);
//中断遍历
// 中断遍历 [AUTO-TRANSLATED:7893aab3]
// Interrupt traversal
return false;
});
if (res_old_snap) {
//已经回复了旧的截图
// 已经回复了旧的截图 [AUTO-TRANSLATED:9051a3e6]
// Old screenshot has been replied
return;
}
//无截图或者截图已经过期
// 无截图或者截图已经过期 [AUTO-TRANSLATED:89c46415]
// No screenshot or screenshot has expired
if (!have_old_snap) {
//无过期截图,生成一个空文件,目的是顺便创建文件夹路径
//同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程
// 无过期截图,生成一个空文件,目的是顺便创建文件夹路径 [AUTO-TRANSLATED:bdbfdbcb]
// No expired screenshot, generate an empty file, the purpose is to create the folder path by the way
// 同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程 [AUTO-TRANSLATED:a04e1ee2]
// At the same time, prevent the FFmpeg process from being started multiple times by continuously trying to call this API during the FFmpeg screenshot generation process
auto file = File::create_file(new_snap, "wb");
if (file) {
fclose(file);
}
}
//启动FFmpeg进程开始截图生成临时文件截图成功后替换为正式文件
// 启动FFmpeg进程开始截图生成临时文件截图成功后替换为正式文件 [AUTO-TRANSLATED:7d589e3f]
// Start the FFmpeg process, start taking screenshots, generate temporary files, replace them with formal files after successful screenshots
auto new_snap_tmp = new_snap + ".tmp";
FFmpegSnap::makeSnap(allArgs["url"], new_snap_tmp, allArgs["timeout_sec"], [invoker, allArgs, new_snap, new_snap_tmp](bool success, const string &err_msg) {
if (!success) {
//生成截图失败,可能残留空文件
// 生成截图失败,可能残留空文件 [AUTO-TRANSLATED:c96a4468]
// Screenshot generation failed, there may be residual empty files
File::delete_file(new_snap_tmp);
} else {
//临时文件改成正式文件
// 临时文件改成正式文件 [AUTO-TRANSLATED:eca24dfd]
// Temporary file changed to formal file
File::delete_file(new_snap);
rename(new_snap_tmp.data(), new_snap.data());
}
@@ -1889,7 +2027,8 @@ void installWebApi() {
WebRtcPluginManager::Instance().negotiateSdp(session, type, *args, [invoker, offer, headerOut, location](const WebRtcInterface &exchanger) mutable {
auto &handler = const_cast<WebRtcInterface &>(exchanger);
try {
// 设置返回类型
// 设置返回类型 [AUTO-TRANSLATED:ffc2a31a]
// Set return type
headerOut["Content-Type"] = "application/sdp";
headerOut["Location"] = location + "?id=" + exchanger.getIdentifier() + "&token=" + exchanger.deleteRandStr();
invoker(201, headerOut, handler.getAnswerSdp(offer));
@@ -1938,17 +2077,22 @@ void installWebApi() {
CHECK_ARGS("vhost", "app", "stream", "file_path");
ProtocolOption option;
// mp4支持多track
// mp4支持多track [AUTO-TRANSLATED:b9688762]
// mp4 supports multiple tracks
option.max_track = 16;
// 默认解复用mp4不生成mp4
// 默认解复用mp4不生成mp4 [AUTO-TRANSLATED:11f2dcee]
// By default, demultiplexing mp4 does not generate mp4
option.enable_mp4 = false;
// 但是如果参数明确指定开启mp4, 那么也允许之
// 但是如果参数明确指定开启mp4, 那么也允许之 [AUTO-TRANSLATED:b143a9e3]
// But if the parameter explicitly specifies to enable mp4, then it is also allowed
option.load(allArgs);
// 强制无人观看时自动关闭
// 强制无人观看时自动关闭 [AUTO-TRANSLATED:f7c85948]
// Force automatic shutdown when no one is watching
option.auto_close = true;
auto tuple = MediaTuple{allArgs["vhost"], allArgs["app"], allArgs["stream"], ""};
auto reader = std::make_shared<MP4Reader>(tuple, allArgs["file_path"], option);
// sample_ms设置为0从配置文件加载file_repeat可以指定如果配置文件也指定循环解复用那么强制开启
// sample_ms设置为0从配置文件加载file_repeat可以指定如果配置文件也指定循环解复用那么强制开启 [AUTO-TRANSLATED:23e826b4]
// sample_ms is set to 0, loaded from the configuration file; file_repeat can be specified, if the configuration file also specifies loop demultiplexing, then force it to be enabled
reader->startReadMP4(0, true, allArgs["file_repeat"]);
});
#endif
@@ -1983,7 +2127,8 @@ void installWebApi() {
return;
}
// 通过on_http_access完成文件下载鉴权请务必确认访问鉴权url参数以及访问文件路径是否合法
// 通过on_http_access完成文件下载鉴权请务必确认访问鉴权url参数以及访问文件路径是否合法 [AUTO-TRANSLATED:73507988]
// File download authentication is completed through on_http_access. Please make sure that the access authentication URL parameters and the access file path are legal
HttpSession::HttpAccessPathInvoker file_invoker = [allArgs, invoker](const string &err_msg, const string &cookie_path_in, int life_second) mutable {
if (!err_msg.empty()) {
invoker(401, StrCaseMap{}, err_msg);
@@ -1999,7 +2144,8 @@ void installWebApi() {
bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, allArgs.parser, file_path, false, file_invoker, sender);
if (!flag) {
// 文件下载鉴权事件无人监听,不允许下载
// 文件下载鉴权事件无人监听,不允许下载 [AUTO-TRANSLATED:5e02f0ce]
// No one is listening to the file download authentication event, download is not allowed
invoker(401, StrCaseMap {}, "None http access event listener");
}
});

View File

@@ -19,16 +19,19 @@
#include "Http/HttpSession.h"
#include "Common/MultiMediaSourceMuxer.h"
//配置文件路径
// 配置文件路径 [AUTO-TRANSLATED:8a373c2f]
// Configuration file path
extern std::string g_ini_file;
namespace mediakit {
////////////RTSP服务器配置///////////
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP server configuration///////////
namespace Rtsp {
extern const std::string kPort;
} //namespace Rtsp
////////////RTMP服务器配置///////////
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP server configuration///////////
namespace Rtmp {
extern const std::string kPort;
} //namespace RTMP
@@ -153,19 +156,25 @@ using ArgsString = HttpAllArgs<std::string>;
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE sender, headerOut, allArgs, val
//注册http请求参数是map<string, variant, StrCaseCompare>类型的http api
// 注册http请求参数是map<string, variant, StrCaseCompare>类型的http api [AUTO-TRANSLATED:8a273897]
// Register http request parameters as map<string, variant, StrCaseCompare> type http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP)> &func);
//注册http请求参数是map<string, variant, StrCaseCompare>类型,但是可以异步回复的的http api
// 注册http请求参数是map<string, variant, StrCaseCompare>类型,但是可以异步回复的的http api [AUTO-TRANSLATED:9da5d5f5]
// Register http request parameters as map<string, variant, StrCaseCompare> type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP_ASYNC)> &func);
//注册http请求参数是Json::Value类型的http api(可以支持多级嵌套的json参数对象)
// 注册http请求参数是Json::Value类型的http api(可以支持多级嵌套的json参数对象) [AUTO-TRANSLATED:c4794456]
// Register http request parameters as Json::Value type http api (can support multi-level nested json parameter objects)
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON)> &func);
//注册http请求参数是Json::Value类型但是可以异步回复的的http api
// 注册http请求参数是Json::Value类型但是可以异步回复的的http api [AUTO-TRANSLATED:742e57fd]
// Register http request parameters as Json::Value type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON_ASYNC)> &func);
//注册http请求参数是http原始请求信息的http api
// 注册http请求参数是http原始请求信息的http api [AUTO-TRANSLATED:72d3fe93]
// Register http request parameters as http original request information http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING)> &func);
//注册http请求参数是http原始请求信息的异步回复的http api
// 注册http请求参数是http原始请求信息的异步回复的http api [AUTO-TRANSLATED:49feefa8]
// Register http request parameters as http original request information asynchronous reply http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING_ASYNC)> &func);
template<typename Args, typename First>
@@ -178,14 +187,17 @@ bool checkArgs(Args &args, const First &first, const KeyTypes &...keys) {
return checkArgs(args, first) && checkArgs(args, keys...);
}
//检查http url中或body中或http header参数是否为空的宏
// 检查http url中或body中或http header参数是否为空的宏 [AUTO-TRANSLATED:9de001a4]
// Check whether the http url, body or http header parameters are empty
#define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \
throw InvalidArgsException("Required parameter missed: " #__VA_ARGS__); \
}
// 检查http参数中是否附带secret密钥的宏127.0.0.1的ip不检查密钥
// 同时检测是否在ip白名单内
// 检查http参数中是否附带secret密钥的宏127.0.0.1的ip不检查密钥 [AUTO-TRANSLATED:7546956c]
// Check whether the http parameters contain the secret key, the ip of 127.0.0.1 does not check the key
// 同时检测是否在ip白名单内 [AUTO-TRANSLATED:d12f963d]
// Check whether it is in the ip whitelist at the same time
#define CHECK_SECRET() \
do { \
auto ip = sender.get_peer_ip(); \

View File

@@ -56,7 +56,8 @@ const string kRetryDelay = HOOK_FIELD "retry_delay";
static onceToken token([]() {
mINI::Instance()[kEnable] = false;
mINI::Instance()[kTimeoutSec] = 10;
// 默认hook地址设置为空采用默认行为(例如不鉴权)
// 默认hook地址设置为空采用默认行为(例如不鉴权) [AUTO-TRANSLATED:0e38bc3c]
// Default hook address is set to empty, using default behavior (e.g. no authentication)
mINI::Instance()[kOnPublish] = "";
mINI::Instance()[kOnPlay] = "";
mINI::Instance()[kOnFlowReport] = "";
@@ -134,7 +135,8 @@ static void parse_http_response(const SockException &ex, const Parser &res, cons
fun(result, "", should_retry);
} catch (std::exception &ex) {
auto errStr = StrPrinter << "[do hook invoker failed]:" << ex.what() << endl;
// 如果还是抛异常,那么再上抛异常
// 如果还是抛异常,那么再上抛异常 [AUTO-TRANSLATED:cc66ff58]
// If an exception is still thrown, then re-throw the exception
fun(Json::nullValue, errStr, should_retry);
}
}
@@ -190,7 +192,8 @@ void do_http_hook(const string &url, const ArgsType &body, const function<void(c
onceToken token(nullptr, [&]() mutable { requester.reset(); });
parse_http_response(ex, res, [&](const Value &obj, const string &err, bool should_retry) {
if (!err.empty()) {
// hook失败
// hook失败 [AUTO-TRANSLATED:68231f46]
// Hook failed
WarnL << "hook " << url << " " << ticker.elapsedTime() << "ms,failed" << err << ":" << bodyStr;
if (retry-- > 0 && should_retry) {
@@ -198,12 +201,14 @@ void do_http_hook(const string &url, const ArgsType &body, const function<void(c
do_http_hook(url, body, func, retry);
return 0;
});
// 重试不需要触发回调
// 重试不需要触发回调 [AUTO-TRANSLATED:41917311]
// Retry does not need to trigger callback
return;
}
} else if (ticker.elapsedTime() > 500) {
// hook成功但是hook响应超过500ms打印警告日志
// hook成功但是hook响应超过500ms打印警告日志 [AUTO-TRANSLATED:e03557aa]
// Hook succeeded, but hook response exceeded 500ms, print warning log
DebugL << "hook " << url << " " << ticker.elapsedTime() << "ms,success:" << bodyStr;
}
@@ -240,7 +245,8 @@ static void reportServerStarted() {
for (auto &pr : mINI::Instance()) {
body[pr.first] = (string &)pr.second;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_server_started, body, nullptr);
}
@@ -252,11 +258,13 @@ static void reportServerExited() {
}
const ArgsType body;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_server_exited, body, nullptr);
}
// 服务器定时保活定时器
// 服务器定时保活定时器 [AUTO-TRANSLATED:2bb39e50]
// Server keep-alive timer
static Timer::Ptr g_keepalive_timer;
static void reportServerKeepalive() {
GET_CONFIG(bool, hook_enable, Hook::kEnable);
@@ -270,7 +278,8 @@ static void reportServerKeepalive() {
getStatisticJson([](const Value &data) mutable {
ArgsType body;
body["data"] = data;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_server_keepalive, body, nullptr);
});
return true;
@@ -285,7 +294,8 @@ static string getPullUrl(const string &origin_fmt, const MediaInfo &info) {
WarnL << "get origin url failed, origin_fmt:" << origin_fmt;
return "";
}
// 告知源站这是来自边沿站的拉流请求,如果未找到流请立即返回拉流失败
// 告知源站这是来自边沿站的拉流请求,如果未找到流请立即返回拉流失败 [AUTO-TRANSLATED:adf0d210]
// Inform the origin station that this is a pull stream request from the edge station, if the stream is not found, please return the pull stream failure immediately
return string(url) + '?' + kEdgeServerParam + '&' + VHOST_KEY + '=' + info.vhost + '&' + info.params;
}
@@ -305,9 +315,11 @@ static void pullStreamFromOrigin(const vector<string> &urls, size_t index, size_
if (!ex) {
return;
}
// 拉流失败
// 拉流失败 [AUTO-TRANSLATED:6d52eb25]
// Pull stream failed
if (++failed_cnt == urls.size()) {
// 已经重试所有源站了
// 已经重试所有源站了 [AUTO-TRANSLATED:b3b384a8]
// All origin stations have been retried
WarnL << "pull stream from origin final failed: " << url;
closePlayer();
return;
@@ -342,20 +354,24 @@ void installWebHook() {
invoker("", ProtocolOption());
return;
}
// 异步执行该hook api防止阻塞NoticeCenter
// 异步执行该hook api防止阻塞NoticeCenter [AUTO-TRANSLATED:783f64c1]
// Asynchronously execute this hook api to prevent blocking NoticeCenter
auto body = make_json(args);
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
body["originType"] = (int)type;
body["originTypeStr"] = getOriginTypeString(type);
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_publish, body, [invoker](const Value &obj, const string &err) mutable {
if (err.empty()) {
// 推流鉴权成功
// 推流鉴权成功 [AUTO-TRANSLATED:e4285dab]
// Push stream authentication succeeded
invoker(err, ProtocolOption(jsonToMini(obj)));
} else {
// 推流鉴权失败
// 推流鉴权失败 [AUTO-TRANSLATED:780430e0]
// Push stream authentication failed
invoker(err, ProtocolOption());
}
});
@@ -371,7 +387,8 @@ void installWebHook() {
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_play, body, [invoker](const Value &obj, const string &err) { invoker(err); });
});
@@ -387,17 +404,20 @@ void installWebHook() {
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_flowreport, body, nullptr);
});
static const string unAuthedRealm = "unAuthedRealm";
// 监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问
// 监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问 [AUTO-TRANSLATED:00dc9fa3]
// Listen to the kBroadcastOnGetRtspRealm event to determine whether the rtsp link needs authentication (traditional rtsp authentication scheme) to access
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastOnGetRtspRealm, [](BroadcastOnGetRtspRealmArgs) {
GET_CONFIG(string, hook_rtsp_realm, Hook::kOnRtspRealm);
if (!hook_enable || hook_rtsp_realm.empty()) {
// 无需认证
// 无需认证 [AUTO-TRANSLATED:77728e07]
// No authentication required
invoker("");
return;
}
@@ -405,10 +425,12 @@ void installWebHook() {
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_rtsp_realm, body, [invoker](const Value &obj, const string &err) {
if (!err.empty()) {
// 如果接口访问失败那么该rtsp流认证失败
// 如果接口访问失败那么该rtsp流认证失败 [AUTO-TRANSLATED:81b19b72]
// If the interface access fails, then the rtsp stream authentication fails
invoker(unAuthedRealm);
return;
}
@@ -416,11 +438,13 @@ void installWebHook() {
});
});
// 监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码
// 监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码 [AUTO-TRANSLATED:bcf1754e]
// Listen to the kBroadcastOnRtspAuth event to return the correct rtsp authentication username and password
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastOnRtspAuth, [](BroadcastOnRtspAuthArgs) {
GET_CONFIG(string, hook_rtsp_auth, Hook::kOnRtspAuth);
if (unAuthedRealm == realm || !hook_enable || hook_rtsp_auth.empty()) {
// 认证失败
// 认证失败 [AUTO-TRANSLATED:70cf56ff]
// Authentication failed
invoker(false, makeRandStr(12));
return;
}
@@ -431,10 +455,12 @@ void installWebHook() {
body["user_name"] = user_name;
body["must_no_encrypt"] = must_no_encrypt;
body["realm"] = realm;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_rtsp_auth, body, [invoker](const Value &obj, const string &err) {
if (!err.empty()) {
// 认证失败
// 认证失败 [AUTO-TRANSLATED:70cf56ff]
// Authentication failed
invoker(false, makeRandStr(12));
return;
}
@@ -442,7 +468,8 @@ void installWebHook() {
});
});
// 监听rtsp、rtmp源注册或注销事件
// 监听rtsp、rtmp源注册或注销事件 [AUTO-TRANSLATED:6396afa8]
// Listen to rtsp, rtmp source registration or deregistration events
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) {
GET_CONFIG(string, hook_stream_changed, Hook::kOnStreamChanged);
if (!hook_enable || hook_stream_changed.empty()) {
@@ -460,7 +487,8 @@ void installWebHook() {
return ret;
});
if (!stream_changed_set.empty() && stream_changed_set.find(sender.getSchema()) == stream_changed_set.end()) {
// 该协议注册注销事件被忽略
// 该协议注册注销事件被忽略 [AUTO-TRANSLATED:87299c9d]
// This protocol registration deregistration event is ignored
return;
}
@@ -473,7 +501,8 @@ void installWebHook() {
dumpMediaTuple(sender.getMediaTuple(), body);
body["regist"] = bRegist;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_stream_changed, body, nullptr);
});
@@ -488,10 +517,12 @@ void installWebHook() {
return ret;
});
// 监听播放失败(未找到特定的流)事件
// 监听播放失败(未找到特定的流)事件 [AUTO-TRANSLATED:ca8cc9ba]
// Listen to playback failure (specific stream not found) event
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastNotFoundStream, [](BroadcastNotFoundStreamArgs) {
if (!origin_urls.empty()) {
// 设置了源站,那么尝试溯源
// 设置了源站,那么尝试溯源 [AUTO-TRANSLATED:541a4ced]
// If the source station is set, then try to trace the source
static atomic<uint8_t> s_index { 0 };
pullStreamFromOrigin(origin_urls, s_index.load(), 0, args, closePlayer);
++s_index;
@@ -499,7 +530,8 @@ void installWebHook() {
}
if (start_with(args.params, kEdgeServerParam)) {
// 源站收到来自边沿站的溯源请求,流不存在时立即返回拉流失败
// 源站收到来自边沿站的溯源请求,流不存在时立即返回拉流失败 [AUTO-TRANSLATED:5bd04a34]
// The source station receives a trace request from the edge station, and immediately returns a pull stream failure if the stream does not exist
closePlayer();
return;
}
@@ -513,7 +545,8 @@ void installWebHook() {
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// Hook回复立即关闭流
// Hook回复立即关闭流 [AUTO-TRANSLATED:2dcf7bd6]
// Hook reply immediately closes the stream
auto res_cb = [closePlayer](const Value &res, const string &err) {
bool flag = res["close"].asBool();
if (flag) {
@@ -521,7 +554,8 @@ void installWebHook() {
}
};
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_stream_not_found, body, res_cb);
});
@@ -539,13 +573,15 @@ void installWebHook() {
};
#ifdef ENABLE_MP4
// 录制mp4文件成功后广播
// 录制mp4文件成功后广播 [AUTO-TRANSLATED:479ec954]
// Broadcast after recording the mp4 file successfully
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRecordMP4, [](BroadcastRecordMP4Args) {
GET_CONFIG(string, hook_record_mp4, Hook::kOnRecordMp4);
if (!hook_enable || hook_record_mp4.empty()) {
return;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_record_mp4, getRecordInfo(info), nullptr);
});
#endif // ENABLE_MP4
@@ -555,7 +591,8 @@ void installWebHook() {
if (!hook_enable || hook_record_ts.empty()) {
return;
}
// 执行 hook
// 执行 hook [AUTO-TRANSLATED:d9d66f75]
// Execute hook
do_http_hook(hook_record_ts, getRecordInfo(info), nullptr);
});
@@ -572,13 +609,15 @@ void installWebHook() {
body["user_name"] = user_name;
body["passwd"] = passwd;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_shell_login, body, [invoker](const Value &, const string &err) { invoker(err); });
});
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastStreamNoneReader, [](BroadcastStreamNoneReaderArgs) {
if (!origin_urls.empty() && sender.getOriginType() == MediaOriginType::pull) {
// 边沿站无人观看时如果是拉流的则立即停止溯源
// 边沿站无人观看时如果是拉流的则立即停止溯源 [AUTO-TRANSLATED:a1429c77]
// If no one is watching at the edge station, stop tracing immediately if it is pulling
sender.close(false);
WarnL << "无人观看主动关闭流:" << sender.getOriginUrl();
return;
@@ -592,7 +631,8 @@ void installWebHook() {
body["schema"] = sender.getSchema();
dumpMediaTuple(sender.getMediaTuple(), body);
weak_ptr<MediaSource> weakSrc = sender.shared_from_this();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_stream_none_reader, body, [weakSrc](const Value &obj, const string &err) {
bool flag = obj["close"].asBool();
auto strongSrc = weakSrc.lock();
@@ -618,7 +658,8 @@ void installWebHook() {
body["originUrl"] = sender.getOriginUrl(MediaSource::NullMediaSource());
body["msg"] = ex.what();
body["err"] = ex.getErrCode();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_send_rtp_stopped, body, nullptr);
});
@@ -629,19 +670,36 @@ void installWebHook() {
* 3、cookie标记是否有权限访问文件如果有权限直接返回文件
* 4、cookie中记录的url参数是否跟本次url参数一致如果一致直接返回客户端错误码
* 5、触发kBroadcastHttpAccess事件
* kBroadcastHttpAccess event trigger mechanism
* 1. Find the cookie according to the http request header, and find it to enter step 3
* 2. Find the cookie according to the http url parameter, if the cookie is still not found, enter step 5
* 3. The cookie marks whether it has permission to access the file, if it has permission, return the file directly
* 4. Whether the url parameter recorded in the cookie is consistent with the current url parameter, if it is consistent, return the client error code directly
* 5. Trigger the kBroadcastHttpAccess event
* [AUTO-TRANSLATED:ecd819e5]
*/
// 开发者应该通过该事件判定http客户端是否有权限访问http服务器上的特定文件
// ZLMediaKit会记录本次鉴权的结果至cookie
// 如果鉴权成功在cookie有效期内那么下次客户端再访问授权目录时ZLMediaKit会直接返回文件
// 如果鉴权失败在cookie有效期内如果http url参数不变(否则会立即再次触发鉴权事件)ZLMediaKit会直接返回错误码
// 如果用户客户端不支持cookie那么ZLMediaKit会根据url参数查找cookie并追踪用户
// 如果没有url参数客户端又不支持cookie那么会根据ip和端口追踪用户
// 追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能
// 开发者应该通过该事件判定http客户端是否有权限访问http服务器上的特定文件 [AUTO-TRANSLATED:938b8bc5]
// Developers should use this event to determine whether the http client has permission to access specific files on the http server
// ZLMediaKit会记录本次鉴权的结果至cookie [AUTO-TRANSLATED:b051ea2e]
// ZLMediaKit will record the result of this authentication to the cookie
// 如果鉴权成功在cookie有效期内那么下次客户端再访问授权目录时ZLMediaKit会直接返回文件 [AUTO-TRANSLATED:be12a468]
// If the authentication is successful, within the validity period of the cookie, the next time the client accesses the authorized directory, ZLMediaKit will return the file directly
// 如果鉴权失败在cookie有效期内如果http url参数不变(否则会立即再次触发鉴权事件)ZLMediaKit会直接返回错误码 [AUTO-TRANSLATED:6396137d]
// If the authentication fails, within the validity period of the cookie, if the http url parameter remains unchanged (otherwise the authentication event will be triggered immediately), ZLMediaKit will return the error code directly
// 如果用户客户端不支持cookie那么ZLMediaKit会根据url参数查找cookie并追踪用户 [AUTO-TRANSLATED:6fd2e366]
// If the user client does not support cookies, then ZLMediaKit will find the cookie according to the url parameter and track the user,
// 如果没有url参数客户端又不支持cookie那么会根据ip和端口追踪用户 [AUTO-TRANSLATED:85a780ea]
// If there is no url parameter and the client does not support cookies, then the user will be tracked according to the ip and port
// 追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能 [AUTO-TRANSLATED:22827145]
// The purpose of tracking users is to cache the last authentication result, reduce the number of authentication times, and improve performance
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastHttpAccess, [](BroadcastHttpAccessArgs) {
GET_CONFIG(string, hook_http_access, Hook::kOnHttpAccess);
if (!hook_enable || hook_http_access.empty()) {
// 未开启http文件访问鉴权那么允许访问但是每次访问都要鉴权
// 因为后续随时都可能开启鉴权(重载配置文件后可能重新开启鉴权)
// 未开启http文件访问鉴权那么允许访问但是每次访问都要鉴权 [AUTO-TRANSLATED:deb3a0ae]
// If http file access authentication is not enabled, then access is allowed, but authentication is required for each access;
// 因为后续随时都可能开启鉴权(重载配置文件后可能重新开启鉴权) [AUTO-TRANSLATED:a090bf06]
// Because authentication may be enabled at any time in the future (authentication may be re-enabled after reloading the configuration file)
if (!HttpFileManager::isIPAllowed(sender.get_peer_ip())) {
invoker("Your ip is not allowed to access the service.", "", 0);
} else {
@@ -660,16 +718,21 @@ void installWebHook() {
for (auto &pr : parser.getHeader()) {
body[string("header.") + pr.first] = pr.second;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_http_access, body, [invoker](const Value &obj, const string &err) {
if (!err.empty()) {
// 如果接口访问失败那么仅限本次没有访问http服务器的权限
// 如果接口访问失败那么仅限本次没有访问http服务器的权限 [AUTO-TRANSLATED:f8afd1fd]
// If the interface access fails, then only this time does not have permission to access the http server
invoker(err, "", 0);
return;
}
// err参数代表不能访问的原因空则代表可以访问
// path参数是该客户端能访问或被禁止的顶端目录如果path为空字符串则表述为当前目录
// second参数规定该cookie超时时间如果second为0本次鉴权结果不缓存
// err参数代表不能访问的原因空则代表可以访问 [AUTO-TRANSLATED:87dd19b9]
// The err parameter represents the reason why it cannot be accessed, empty means it can be accessed
// path参数是该客户端能访问或被禁止的顶端目录如果path为空字符串则表述为当前目录 [AUTO-TRANSLATED:b883a448]
// The path parameter is the top directory that this client can access or is prohibited, if path is an empty string, it means the current directory
// second参数规定该cookie超时时间如果second为0本次鉴权结果不缓存 [AUTO-TRANSLATED:1a0b9eb1]
// The second parameter specifies the timeout time of this cookie, if second is 0, the result of this authentication will not be cached
invoker(obj["err"].asString(), obj["path"].asString(), obj["second"].asInt());
});
});
@@ -691,10 +754,12 @@ void installWebHook() {
do_http_hook(rtp_server_timeout, body);
});
// 汇报服务器重新启动
// 汇报服务器重新启动 [AUTO-TRANSLATED:bd7d83df]
// Report server restart
reportServerStarted();
// 定时上报保活
// 定时上报保活 [AUTO-TRANSLATED:bd2364a0]
// Report keep-alive regularly
reportServerKeepalive();
}

View File

@@ -15,7 +15,8 @@
#include <functional>
#include "json/json.h"
//支持json或urlencoded方式传输参数
// 支持json或urlencoded方式传输参数 [AUTO-TRANSLATED:0e14d484]
// // Support json or urlencoded way to transmit parameters
#define JSON_ARGS
#ifdef JSON_ARGS
@@ -25,7 +26,8 @@ typedef mediakit::HttpArgs ArgsType;
#endif
namespace Hook {
//web hook回复最大超时时间
// web hook回复最大超时时间 [AUTO-TRANSLATED:9a059363]
// Maximum timeout for web hook reply
extern const std::string kTimeoutSec;
}//namespace Hook
@@ -37,6 +39,13 @@ void onProcessExited();
* @param url 请求地址
* @param body 请求body
* @param func 回调
* Trigger http hook request
* @param url Request address
* @param body Request body
* @param func Callback
* [AUTO-TRANSLATED:8ffdd09b]
*/
void do_http_hook(const std::string &url, const ArgsType &body, const std::function<void(const Json::Value &, const std::string &)> &func = nullptr);
#endif //ZLMEDIAKIT_WEBHOOK_H

View File

@@ -50,7 +50,8 @@ using namespace toolkit;
using namespace mediakit;
namespace mediakit {
////////////HTTP配置///////////
// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694]
// //////////HTTP configuration///////////
namespace Http {
#define HTTP_FIELD "http."
const string kPort = HTTP_FIELD"port";
@@ -61,7 +62,8 @@ onceToken token1([](){
},nullptr);
}//namespace Http
////////////SHELL配置///////////
// //////////SHELL配置/////////// [AUTO-TRANSLATED:f023ec45]
// //////////SHELL configuration///////////
namespace Shell {
#define SHELL_FIELD "shell."
const string kPort = SHELL_FIELD"port";
@@ -70,7 +72,8 @@ onceToken token1([](){
},nullptr);
} //namespace Shell
////////////RTSP服务器配置///////////
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP server configuration///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
const string kPort = RTSP_FIELD"port";
@@ -82,7 +85,8 @@ onceToken token1([](){
} //namespace Rtsp
////////////RTMP服务器配置///////////
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP server configuration///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
const string kPort = RTMP_FIELD"port";
@@ -93,7 +97,8 @@ onceToken token1([](){
},nullptr);
} //namespace RTMP
////////////Rtp代理相关配置///////////
// //////////Rtp代理相关配置/////////// [AUTO-TRANSLATED:7b285587]
// //////////Rtp proxy related configuration///////////
namespace RtpProxy {
#define RTP_PROXY_FIELD "rtp_proxy."
const string kPort = RTP_PROXY_FIELD"port";
@@ -171,7 +176,8 @@ public:
#if defined(ENABLE_VERSION)
(*_parser) << Option('v', "version", Option::ArgNone, nullptr, false, "显示版本号",
[](const std::shared_ptr<ostream> &stream, const string &arg) -> bool {
//版本信息
// 版本信息 [AUTO-TRANSLATED:d4cc59b2]
// Version information
*stream << "编译日期: " << BUILD_TIME << std::endl;
*stream << "代码日期: " << COMMIT_TIME << std::endl;
*stream << "当前git分支: " << BRANCH_NAME << std::endl;
@@ -205,7 +211,8 @@ public:
}
};
//全局变量在WebApi中用于保存配置文件用
// 全局变量在WebApi中用于保存配置文件用 [AUTO-TRANSLATED:6d5585ca]
// Global variable, used in WebApi to save configuration files
string g_ini_file;
int start_main(int argc,char *argv[]) {
@@ -228,11 +235,13 @@ int start_main(int argc,char *argv[]) {
int threads = cmd_main["threads"];
bool affinity = cmd_main["affinity"];
//设置日志
// 设置日志 [AUTO-TRANSLATED:50372045]
// Set log
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#if !defined(ANDROID)
auto fileChannel = std::make_shared<FileChannel>("FileChannel", cmd_main["log-dir"], logLevel);
// 日志最多保存天数
// 日志最多保存天数 [AUTO-TRANSLATED:9bfa8a9a]
// Maximum number of days to save logs
fileChannel->setMaxDay(cmd_main["max_day"]);
fileChannel->setFileMaxCount(cmd_main["log-slice"]);
fileChannel->setFileMaxSize(cmd_main["log-size"]);
@@ -243,24 +252,29 @@ int start_main(int argc,char *argv[]) {
pid_t pid = getpid();
bool kill_parent_if_failed = true;
if (bDaemon) {
//启动守护进程
// 启动守护进程 [AUTO-TRANSLATED:33b2c5be]
// Start daemon process
System::startDaemon(kill_parent_if_failed);
}
//开启崩溃捕获等
// 开启崩溃捕获等 [AUTO-TRANSLATED:9c7c759c]
// Enable crash capture, etc.
System::systemSetup();
#endif//!defined(_WIN32)
//启动异步日志线程
// 启动异步日志线程 [AUTO-TRANSLATED:c93cc6f4]
// Start asynchronous log thread
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
InfoL << kServerName;
//加载配置文件,如果配置文件不存在就创建一个
// 加载配置文件,如果配置文件不存在就创建一个 [AUTO-TRANSLATED:761e7479]
// Load configuration file, create one if it doesn't exist
loadIniConfig(g_ini_file.data());
auto &secret = mINI::Instance()[API::kSecret];
if (secret == "035c73f7-bb6b-4889-a715-d9eb2d1925cc" || secret.empty()) {
// 使用默认secret被禁止启动
// 使用默认secret被禁止启动 [AUTO-TRANSLATED:6295164b]
// Starting with the default secret is prohibited
secret = makeRandStr(32, true);
mINI::Instance().dumpFile(g_ini_file);
WarnL << "The " << API::kSecret << " is invalid, modified it to: " << secret
@@ -268,13 +282,16 @@ int start_main(int argc,char *argv[]) {
}
if (!File::is_dir(ssl_file)) {
// 不是文件夹,加载证书,证书包含公钥和私钥
// 不是文件夹,加载证书,证书包含公钥和私钥 [AUTO-TRANSLATED:5d3a5e49]
// Not a folder, load certificate, certificate contains public key and private key
SSL_Initor::Instance().loadCertificate(ssl_file.data());
} else {
//加载文件夹下的所有证书
// 加载文件夹下的所有证书 [AUTO-TRANSLATED:0e1f9b20]
// Load all certificates under the folder
File::scanDir(ssl_file,[](const string &path, bool isDir){
if (!isDir) {
// 最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
// 最后的一个证书会当做默认证书(客户端ssl握手时未指定主机) [AUTO-TRANSLATED:b242685c]
// The last certificate will be used as the default certificate (client ssl handshake does not specify the host)
SSL_Initor::Instance().loadCertificate(path.data());
}
return true;
@@ -291,37 +308,46 @@ int start_main(int argc,char *argv[]) {
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort];
//设置poller线程数和cpu亲和性,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
//如果需要调用getSnap和addFFmpegSource接口可以关闭cpu亲和性
// 设置poller线程数和cpu亲和性,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 [AUTO-TRANSLATED:7f03a1e5]
// Set the number of poller threads and CPU affinity. This function must be called before using ZLToolKit network related objects to take effect.
// 如果需要调用getSnap和addFFmpegSource接口可以关闭cpu亲和性 [AUTO-TRANSLATED:7629f7bc]
// If you need to call the getSnap and addFFmpegSource interfaces, you can turn off CPU affinity
EventPollerPool::setPoolSize(threads);
WorkThreadPool::setPoolSize(threads);
EventPollerPool::enableCpuAffinity(affinity);
//简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象
//测试方法:telnet 127.0.0.1 9000
// 简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象 [AUTO-TRANSLATED:f9324c6e]
// Simple telnet server, can be used for server debugging, but cannot use port 23, otherwise telnet will have inexplicable phenomena
// 测试方法:telnet 127.0.0.1 9000 [AUTO-TRANSLATED:de0ac883]
// Test method: telnet 127.0.0.1 9000
auto shellSrv = std::make_shared<TcpServer>();
//rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问
// rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问 [AUTO-TRANSLATED:f28e54f7]
// rtsp[s] server, can be used for devices such as Amazon Echo Show to access
auto rtspSrv = std::make_shared<TcpServer>();
auto rtspSSLSrv = std::make_shared<TcpServer>();
//rtmp[s]服务器
// rtmp[s]服务器 [AUTO-TRANSLATED:3ac98bf5]
// rtmp[s] server
auto rtmpSrv = std::make_shared<TcpServer>();
auto rtmpsSrv = std::make_shared<TcpServer>();
//http[s]服务器
// http[s]服务器 [AUTO-TRANSLATED:5bbc8735]
// http[s] server
auto httpSrv = std::make_shared<TcpServer>();
auto httpsSrv = std::make_shared<TcpServer>();
#if defined(ENABLE_RTPPROXY)
//GB28181 rtp推流端口支持UDP/TCP
// GB28181 rtp推流端口支持UDP/TCP [AUTO-TRANSLATED:8a9b2872]
// GB28181 rtp push stream port, supports UDP/TCP
auto rtpServer = std::make_shared<RtpServer>();
#endif//defined(ENABLE_RTPPROXY)
#if defined(ENABLE_WEBRTC)
auto rtcSrv_tcp = std::make_shared<TcpServer>();
//webrtc udp服务器
// webrtc udp服务器 [AUTO-TRANSLATED:157a64e5]
// webrtc udp server
auto rtcSrv_udp = std::make_shared<UdpServer>();
rtcSrv_udp->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
if (!buf) {
@@ -329,7 +355,8 @@ int start_main(int argc,char *argv[]) {
}
auto new_poller = WebRtcSession::queryPoller(buf);
if (!new_poller) {
//该数据对应的webrtc对象未找到丢弃之
// 该数据对应的webrtc对象未找到丢弃之 [AUTO-TRANSLATED:d401f8cb]
// The webrtc object corresponding to this data is not found, discard it
return Socket::Ptr();
}
return Socket::createSocket(new_poller, false);
@@ -347,7 +374,8 @@ int start_main(int argc,char *argv[]) {
}
auto new_poller = SRT::SrtSession::queryPoller(buf);
if (!new_poller) {
//握手第一阶段
// 握手第一阶段 [AUTO-TRANSLATED:6b3abcd4]
// Handshake phase one
return Socket::createSocket(poller, false);
}
return Socket::createSocket(new_poller, false);
@@ -362,31 +390,40 @@ int start_main(int argc,char *argv[]) {
InfoL << "已启动http hook 接口";
try {
//rtsp服务器端口默认554
// rtsp服务器端口默认554 [AUTO-TRANSLATED:07937d81]
// rtsp server, default port 554
if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort, listen_ip); }
//rtsps服务器端口默认322
// rtsps服务器端口默认322 [AUTO-TRANSLATED:e8a9fd71]
// rtsps server, default port 322
if (rtspsPort) { rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort, listen_ip); }
//rtmp服务器端口默认1935
// rtmp服务器端口默认1935 [AUTO-TRANSLATED:58324c74]
// rtmp server, default port 1935
if (rtmpPort) { rtmpSrv->start<RtmpSession>(rtmpPort, listen_ip); }
//rtmps服务器端口默认19350
// rtmps服务器端口默认19350 [AUTO-TRANSLATED:c565ff4e]
// rtmps server, default port 19350
if (rtmpsPort) { rtmpsSrv->start<RtmpSessionWithSSL>(rtmpsPort, listen_ip); }
//http服务器端口默认80
// http服务器端口默认80 [AUTO-TRANSLATED:8899e852]
// http server, default port 80
if (httpPort) { httpSrv->start<HttpSession>(httpPort, listen_ip); }
//https服务器端口默认443
// https服务器端口默认443 [AUTO-TRANSLATED:24999616]
// https server, default port 443
if (httpsPort) { httpsSrv->start<HttpsSession>(httpsPort, listen_ip); }
//telnet远程调试服务器
// telnet远程调试服务器 [AUTO-TRANSLATED:577cb7cf]
// telnet remote debug server
if (shellPort) { shellSrv->start<ShellSession>(shellPort, listen_ip); }
#if defined(ENABLE_RTPPROXY)
//创建rtp服务器
// 创建rtp服务器 [AUTO-TRANSLATED:873f7f52]
// create rtp server
if (rtpPort) { rtpServer->start(rtpPort, listen_ip.c_str()); }
#endif//defined(ENABLE_RTPPROXY)
#if defined(ENABLE_WEBRTC)
//webrtc udp服务器
// webrtc udp服务器 [AUTO-TRANSLATED:157a64e5]
// webrtc udp server
if (rtcPort) { rtcSrv_udp->start<WebRtcSession>(rtcPort, listen_ip);}
if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort, listen_ip);}
@@ -394,7 +431,8 @@ int start_main(int argc,char *argv[]) {
#endif//defined(ENABLE_WEBRTC)
#if defined(ENABLE_SRT)
// srt udp服务器
// srt udp服务器 [AUTO-TRANSLATED:06911727]
// srt udp server
if (srtPort) { srtSrv->start<SRT::SrtSession>(srtPort, listen_ip); }
#endif//defined(ENABLE_SRT)
@@ -403,14 +441,16 @@ int start_main(int argc,char *argv[]) {
sleep(1);
#if !defined(_WIN32)
if (pid != getpid() && kill_parent_if_failed) {
//杀掉守护进程
// 杀掉守护进程 [AUTO-TRANSLATED:bee035e9]
// kill the daemon process
kill(pid, SIGINT);
}
#endif
return -1;
}
//设置退出信号处理函数
// 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770]
// set exit signal handler
static semaphore sem;
signal(SIGINT, [](int) {
InfoL << "SIGINT:exit";
@@ -433,7 +473,8 @@ int start_main(int argc,char *argv[]) {
unInstallWebHook();
onProcessExited();
//休眠1秒再退出防止资源释放顺序错误
// 休眠1秒再退出防止资源释放顺序错误 [AUTO-TRANSLATED:1b11a74f]
// sleep for 1 second before exiting, to prevent resource release order errors
InfoL << "程序退出中,请等待...";
sleep(1);
InfoL << "程序退出完毕!";