mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-03-26 07:50:52 +08:00
AI automatically translates all comments in the code into English (#3917)
This commit is contained in:
@@ -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) : "");
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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(); \
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
123
server/main.cpp
123
server/main.cpp
@@ -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 << "程序退出完毕!";
|
||||
|
||||
Reference in New Issue
Block a user