diff --git a/python/mk_plugin.py b/python/mk_plugin.py index f3f91237..bd0d141d 100644 --- a/python/mk_plugin.py +++ b/python/mk_plugin.py @@ -114,5 +114,14 @@ def on_stream_not_found(args: dict, sender:dict, invoker) -> bool: # 返回True代表此事件被python拦截 return True +def on_record_mp4(info: dict) -> bool: + mk_logger.log_info(f"on_record_mp4, info: {info}") + # 返回True代表此事件被python拦截 + return True +def on_record_ts(info: dict) -> bool: + mk_logger.log_info(f"on_record_ts, info: {info}") + # 返回True代表此事件被python拦截 + return True + def on_reload_config(): mk_logger.log_info(f"on_reload_config") \ No newline at end of file diff --git a/server/WebHook.cpp b/server/WebHook.cpp index b5e495bd..973e0ef3 100755 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -359,6 +359,19 @@ static mINI jsonToMini(const Value &obj) { return ret; } +ArgsType getRecordInfo(const RecordInfo &info) { + ArgsType body; + body["start_time"] = (Json::UInt64)info.start_time; + body["file_size"] = (Json::UInt64)info.file_size; + body["time_len"] = info.time_len; + body["file_path"] = info.file_path; + body["file_name"] = info.file_name; + body["folder"] = info.folder; + body["url"] = info.url; + dumpMediaTuple(info, body); + return body; +} + void installWebHook() { GET_CONFIG(bool, hook_enable, Hook::kEnable); @@ -610,23 +623,15 @@ void installWebHook() { do_http_hook(hook_stream_not_found, body, res_cb); }); - static auto getRecordInfo = [](const RecordInfo &info) { - ArgsType body; - body["start_time"] = (Json::UInt64)info.start_time; - body["file_size"] = (Json::UInt64)info.file_size; - body["time_len"] = info.time_len; - body["file_path"] = info.file_path; - body["file_name"] = info.file_name; - body["folder"] = info.folder; - body["url"] = info.url; - dumpMediaTuple(info, body); - return body; - }; - #ifdef ENABLE_MP4 // 录制mp4文件成功后广播 [AUTO-TRANSLATED:479ec954] // Broadcast after recording the mp4 file successfully NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRecordMP4, [](BroadcastRecordMP4Args) { +#if defined(ENABLE_PYTHON) + if (PythonInvoker::Instance().on_record_mp4(info)) { + return; + } +#endif GET_CONFIG(string, hook_record_mp4, Hook::kOnRecordMp4); if (!hook_enable || hook_record_mp4.empty()) { return; @@ -638,6 +643,11 @@ void installWebHook() { #endif // ENABLE_MP4 NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRecordTs, [](BroadcastRecordTsArgs) { +#if defined(ENABLE_PYTHON) + if (PythonInvoker::Instance().on_record_ts(info)) { + return; + } +#endif GET_CONFIG(string, hook_record_ts, Hook::kOnRecordTs); if (!hook_enable || hook_record_ts.empty()) { return; diff --git a/server/pyinvoker.cpp b/server/pyinvoker.cpp index 2691b5b4..047e350a 100644 --- a/server/pyinvoker.cpp +++ b/server/pyinvoker.cpp @@ -19,6 +19,7 @@ using namespace mediakit; extern ArgsType make_json(const MediaInfo &args); extern void fillSockInfo(Json::Value & val, SockInfo* info); +extern ArgsType getRecordInfo(const RecordInfo &info); extern std::string g_ini_file; template @@ -73,6 +74,10 @@ py::dict to_python(const SockInfo &info) { return jsonToPython(json); } +py::dict to_python(const RecordInfo &info) { + return jsonToPython(getRecordInfo(info)); +} + template std::shared_ptr to_python_ref(const T &t) { return std::shared_ptr(const_cast(&t), py::nodelete()); @@ -354,6 +359,8 @@ PythonInvoker::~PythonInvoker() { _on_get_rtsp_realm = py::function(); _on_rtsp_auth = py::function(); _on_stream_not_found = py::function(); + _on_record_mp4 = py::function(); + _on_record_ts = py::function(); _module = py::module(); } delete _rel; @@ -379,6 +386,8 @@ void PythonInvoker::load(const std::string &module_name) { GET_FUNC(_module, on_get_rtsp_realm); GET_FUNC(_module, on_rtsp_auth); GET_FUNC(_module, on_stream_not_found); + GET_FUNC(_module, on_record_mp4); + GET_FUNC(_module, on_record_ts); if (hasattr(_module, "on_start")) { py::object on_start = _module.attr("on_start"); @@ -455,6 +464,22 @@ bool PythonInvoker::on_stream_not_found(BroadcastNotFoundStreamArgs) const { return _on_stream_not_found(to_python(args), to_python(sender), to_python(closePlayer)).cast(); } +bool PythonInvoker::on_record_mp4(BroadcastRecordMP4Args) const { + py::gil_scoped_acquire gil; // 确保在 Python 调用期间持有 GIL + if (!_on_record_mp4) { + return false; + } + return _on_record_mp4(to_python(info)).cast(); +} + +bool PythonInvoker::on_record_ts(BroadcastRecordTsArgs) const { + py::gil_scoped_acquire gil; // 确保在 Python 调用期间持有 GIL + if (!_on_record_ts) { + return false; + } + return _on_record_ts(to_python(info)).cast(); +} + } // namespace mediakit #endif diff --git a/server/pyinvoker.h b/server/pyinvoker.h index 372f8afe..d39a664c 100644 --- a/server/pyinvoker.h +++ b/server/pyinvoker.h @@ -34,6 +34,8 @@ public: bool on_get_rtsp_realm(BroadcastOnGetRtspRealmArgs) const; bool on_rtsp_auth(BroadcastOnRtspAuthArgs) const; bool on_stream_not_found(BroadcastNotFoundStreamArgs) const; + bool on_record_mp4(BroadcastRecordMP4Args) const; + bool on_record_ts(BroadcastRecordTsArgs) const; private: PythonInvoker(); @@ -64,6 +66,10 @@ private: py::function _on_rtsp_auth; // 播放一个不存在的流时触发 py::function _on_stream_not_found; + // 生成mp4录制文件回调 + py::function _on_record_mp4; + // 生成hls ts/fmp4切片文件回调 + py::function _on_record_ts; };