diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 2a8d3224..92aa3457 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -91,8 +91,9 @@ void FFmpegSource::play(const string &ffmpeg_cmd_key, const string &src_url,cons } char cmd[1024] = {0}; - snprintf(cmd, sizeof(cmd), ffmpeg_cmd.data(), ffmpeg_bin.data(), src_url.data(), dst_url.data()); - _process.run(cmd,ffmpeg_log.empty() ? "" : File::absolutePath("",ffmpeg_log)); + snprintf(cmd, sizeof(cmd), ffmpeg_cmd.data(), File::absolutePath("", ffmpeg_bin).data(), src_url.data(), dst_url.data()); + auto log_file = ffmpeg_log.empty() ? "" : File::absolutePath("", ffmpeg_log); + _process.run(cmd, log_file); InfoL << cmd; if (is_local_ip(_media_info._host)) { @@ -312,7 +313,7 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) { } } -void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float timeout_sec, const function &cb) { +void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float timeout_sec, const onSnap &cb) { GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin); GET_CONFIG(string,ffmpeg_snap,FFmpeg::kSnap); GET_CONFIG(string,ffmpeg_log,FFmpeg::kLog); @@ -321,28 +322,33 @@ void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float auto elapsed_ms = ticker.elapsedTime(); if (elapsed_ms > timeout_sec * 1000) { //超时,后台线程负载太高,当代太久才启动该任务 - cb(false); + cb(false, "wait work poller schedule snap task timeout"); return; } - char cmd[1024] = {0}; - snprintf(cmd, sizeof(cmd),ffmpeg_snap.data(),ffmpeg_bin.data(),play_url.data(),save_path.data()); + char cmd[2048] = { 0 }; + snprintf(cmd, sizeof(cmd), ffmpeg_snap.data(), File::absolutePath("", ffmpeg_bin).data(), play_url.data(), save_path.data()); + std::shared_ptr process = std::make_shared(); - process->run(cmd,ffmpeg_log.empty() ? "" : File::absolutePath("",ffmpeg_log)); + auto log_file = ffmpeg_log.empty() ? ffmpeg_log : File::absolutePath("", ffmpeg_log); + process->run(cmd, log_file); + //定时器延时应该减去后台任务启动的延时 - auto delayTask = EventPollerPool::Instance().getPoller()->doDelayTask((uint64_t)(timeout_sec * 1000 - elapsed_ms),[process,cb](){ - if(process->wait(false)){ - //FFmpeg进程还在运行,超时就关闭它 - process->kill(2000); - } - return 0; - }); + auto delayTask = EventPollerPool::Instance().getPoller()->doDelayTask( + (uint64_t)(timeout_sec * 1000 - elapsed_ms), [process, cb, log_file, save_path]() { + if (process->wait(false)) { + // FFmpeg进程还在运行,超时就关闭它 + process->kill(2000); + } + return 0; + }); //等待FFmpeg进程退出 process->wait(true); - //FFmpeg进程退出了可以取消定时器了 + // FFmpeg进程退出了可以取消定时器了 delayTask->cancel(); //执行回调函数 - cb(process->exit_code() == 0); + bool success = process->exit_code() == 0 && File::fileSize(save_path.data()); + cb(success, (!success && !log_file.empty()) ? File::loadFile(log_file.data()) : ""); }); } diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index ad27202d..3bf5fe19 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -25,12 +25,14 @@ namespace FFmpeg { class FFmpegSnap { public: + using onSnap = std::function; /// 创建截图 /// \param play_url 播放url地址,只要FFmpeg支持即可 /// \param save_path 截图jpeg文件保存路径 /// \param timeout_sec 生成截图超时时间(防止阻塞太久) /// \param cb 生成截图成功与否回调 - static void makeSnap(const std::string &play_url, const std::string &save_path, float timeout_sec, const std::function &cb); + static void makeSnap(const std::string &play_url, const std::string &save_path, float timeout_sec, const onSnap &cb); + private: FFmpegSnap() = delete; ~FFmpegSnap() = delete; diff --git a/server/Process.cpp b/server/Process.cpp index 48f99946..fa685b28 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -30,7 +30,7 @@ using namespace toolkit; using namespace std; -void Process::run(const string &cmd, const string &log_file_tmp) { +void Process::run(const string &cmd, string &log_file_tmp) { kill(2000); #ifdef _WIN32 STARTUPINFO si = {0}; @@ -42,6 +42,7 @@ void Process::run(const string &cmd, const string &log_file_tmp) { } else { log_file = StrPrinter << log_file_tmp << "." << getCurrentMillisecond(); } + log_file_tmp = log_file; //重定向shell日志至文件 auto fp = File::create_file(log_file.data(), "ab"); @@ -92,6 +93,8 @@ void Process::run(const string &cmd, const string &log_file_tmp) { //在启动子进程时,暂时禁用SIGINT、SIGTERM信号 signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); + signal(SIGSEGV, SIG_IGN); + signal(SIGABRT, SIG_IGN); //重定向shell日志至文件 auto fp = File::create_file(log_file.data(), "ab"); @@ -148,6 +151,7 @@ void Process::run(const string &cmd, const string &log_file_tmp) { } else { log_file = StrPrinter << log_file_tmp << "." << _pid; } + log_file_tmp = log_file; InfoL << "start child process " << _pid << ", log file:" << log_file; #endif // _WIN32 } diff --git a/server/Process.h b/server/Process.h index 564c791a..aaae3e74 100644 --- a/server/Process.h +++ b/server/Process.h @@ -24,7 +24,7 @@ class Process { public: Process(); ~Process(); - void run(const std::string &cmd,const std::string &log_file); + void run(const std::string &cmd, std::string &log_file); void kill(int max_delay,bool force = false); bool wait(bool block = true); int exit_code(); diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 49b0ed5d..580acef6 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -1251,14 +1251,23 @@ void installWebApi() { static auto responseSnap = [](const string &snap_path, const HttpSession::KeyValue &headerIn, - const HttpSession::HttpResponseInvoker &invoker) { + const HttpSession::HttpResponseInvoker &invoker, + const string &err_msg = "") { + static bool s_snap_success_once = false; StrCaseMap headerOut; GET_CONFIG(string, defaultSnap, API::kDefaultSnap); - if (!File::fileSize(snap_path.data()) && !defaultSnap.empty()) { - //空文件且设置了预设图,则返回预设图片(也就是FFmpeg生成截图中空档期的默认图片) - const_cast(snap_path) = File::absolutePath(defaultSnap, ""); + if (!File::fileSize(snap_path.data())) { + if (!err_msg.empty() && (!s_snap_success_once || defaultSnap.empty())) { + //重来没截图成功过或者默认截图图片为空,那么直接返回FFmpeg错误日志 + headerOut["Content-Type"] = HttpFileManager::getContentType(".txt"); + invoker.responseFile(headerIn, headerOut, err_msg, false, false); + return; + } + //截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片 + const_cast(snap_path) = File::absolutePath("", defaultSnap); headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data()); } else { + s_snap_success_once = true; //之前生成的截图文件,我们默认为jpeg格式 headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); } @@ -1317,7 +1326,7 @@ void installWebApi() { //启动FFmpeg进程,开始截图,生成临时文件,截图成功后替换为正式文件 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) { + 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) { //生成截图失败,可能残留空文件 File::delete_file(new_snap_tmp.data()); @@ -1326,7 +1335,7 @@ void installWebApi() { File::delete_file(new_snap.data()); rename(new_snap_tmp.data(), new_snap.data()); } - responseSnap(new_snap, allArgs.getParser().getHeader(), invoker); + responseSnap(new_snap, allArgs.getParser().getHeader(), invoker, err_msg); }); });