diff --git a/airport_application.yml b/airport_application.yml new file mode 100644 index 0000000..d59425e --- /dev/null +++ b/airport_application.yml @@ -0,0 +1,25 @@ +video: + pullUrl: "rtsp://127.0.0.1:8554/video" + pushUrl: ["rtmp://192.168.10.101:19350/rlive/stream_9?sign=f8a15b6n"] +# 日志设置 +log: + # 是否开启文件输出 True:开启 False:关闭 + enable_file_log: True + # 是否开启控制台日志输出 True:开启 False:关闭 + enable_stderr: True + # 日志打印文件夹 + base_path: ./logs/ + # 日志文件名称 + log_name: airport.log + # 日志打印格式 + log_fmt: "{time:YYYY-MM-DD HH:mm:ss.SSS} [{level}][{process.name}-{process.id}-{thread.name}-{thread.id}][{line}] {module}-{function} - {message}" + # 日志隔离级别 + level: INFO + # 日志每天0点创建新文件 + rotation: 00:00 + # 日志保存时间1天 + retention: 1 days + # 线程安全 + enqueue: True + # 编码格式 + encoding: utf8 diff --git a/airport_master.py b/airport_master.py new file mode 100644 index 0000000..04163e1 --- /dev/null +++ b/airport_master.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +from alg_airport_ffmpeg.service.PushStreamService import PushStreamService + +""" + airport主程序入口 +""" + + +if __name__ == '__main__': + print("(♥◠‿◠)ノ゙ AIRPORT【推流服务】开始启动 ლ(´ڡ`ლ)゙") + PushStreamService().start_service() diff --git a/common/Constant.py b/common/Constant.py new file mode 100644 index 0000000..f00e255 --- /dev/null +++ b/common/Constant.py @@ -0,0 +1,20 @@ +from enum import Enum, unique + + +# 常量枚举 +@unique +class ConstantEnum(Enum): + APPLICATION_CONFIG = ("airport_application.yml", "配置文件名称") + + UTF_8 = ("utf-8", "utf-8") + + R = ("r", "可读") + + START_LOG = (""" + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) +""", "启动服务LOG") diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/__pycache__/Constant.cpython-38.pyc b/common/__pycache__/Constant.cpython-38.pyc new file mode 100644 index 0000000..7647a68 Binary files /dev/null and b/common/__pycache__/Constant.cpython-38.pyc differ diff --git a/common/__pycache__/__init__.cpython-38.pyc b/common/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..ccba540 Binary files /dev/null and b/common/__pycache__/__init__.cpython-38.pyc differ diff --git a/concurrency/CommonThread.py b/concurrency/CommonThread.py new file mode 100644 index 0000000..acfe084 --- /dev/null +++ b/concurrency/CommonThread.py @@ -0,0 +1,22 @@ +import time +from threading import Thread +from loguru import logger + + +class Common(Thread): + + def __init__(self, timeout, func, args=()): + super(Common, self).__init__() + self.__timeout = timeout + self.__func = func + self.__args = args + self.__result = None + + def get_result(self): + self.join(self.__timeout) + return self.__result + + def run(self): + # logger.info("开始执行线程!") + self.__result = self.__func(self.__args) + # logger.info("线程停止完成!") diff --git a/concurrency/__init__.py b/concurrency/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/concurrency/__pycache__/CommonThread.cpython-38.pyc b/concurrency/__pycache__/CommonThread.cpython-38.pyc new file mode 100644 index 0000000..81bc0ca Binary files /dev/null and b/concurrency/__pycache__/CommonThread.cpython-38.pyc differ diff --git a/concurrency/__pycache__/__init__.cpython-38.pyc b/concurrency/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..16f9482 Binary files /dev/null and b/concurrency/__pycache__/__init__.cpython-38.pyc differ diff --git a/enums/ExceptionEnum.py b/enums/ExceptionEnum.py new file mode 100644 index 0000000..7faa90f --- /dev/null +++ b/enums/ExceptionEnum.py @@ -0,0 +1,13 @@ +from enum import Enum, unique + + +# 异常枚举 +@unique +class ExceptionType(Enum): + + PULL_STREAM_URL_EXCEPTION = ("AP000", "拉流地址不能为空!") + + PUSH_STREAM_URL_EXCEPTION = ("AP001", "推流地址不能为空!") + + SERVICE_INNER_EXCEPTION = ("AP999", "系统内部异常, 请联系工程师定位处理!") + diff --git a/enums/__init__.py b/enums/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/enums/__pycache__/ExceptionEnum.cpython-38.pyc b/enums/__pycache__/ExceptionEnum.cpython-38.pyc new file mode 100644 index 0000000..80621a2 Binary files /dev/null and b/enums/__pycache__/ExceptionEnum.cpython-38.pyc differ diff --git a/enums/__pycache__/__init__.cpython-38.pyc b/enums/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..d3f7813 Binary files /dev/null and b/enums/__pycache__/__init__.cpython-38.pyc differ diff --git a/exception/CustomerException.py b/exception/CustomerException.py new file mode 100644 index 0000000..c5968e4 --- /dev/null +++ b/exception/CustomerException.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from loguru import logger + + +""" + 自定义异常 +""" + + +class ServiceException(Exception): + def __init__(self, code, msg): + self.code = code + self.msg = msg + + def __str__(self): + logger.error("异常编码:{}, 异常描述:{}", self.code, self.msg) + + + diff --git a/exception/__init__.py b/exception/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/exception/__pycache__/CustomerException.cpython-38.pyc b/exception/__pycache__/CustomerException.cpython-38.pyc new file mode 100644 index 0000000..a2e0935 Binary files /dev/null and b/exception/__pycache__/CustomerException.cpython-38.pyc differ diff --git a/exception/__pycache__/__init__.cpython-38.pyc b/exception/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..522f0b3 Binary files /dev/null and b/exception/__pycache__/__init__.cpython-38.pyc differ diff --git a/logs/airport.log b/logs/airport.log new file mode 100644 index 0000000..ece22e0 --- /dev/null +++ b/logs/airport.log @@ -0,0 +1,203 @@ +2023-01-13 13:12:40.886 [INFO][MainProcess-10664-MainThread-19964][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 13:12:41.894 [INFO][MainProcess-10664-MainThread-19964][38] PushStreamService-start_service - 开始重新获取视频信息: 0次 +2023-01-13 13:12:49.945 [INFO][MainProcess-10664-MainThread-19964][38] PushStreamService-start_service - 开始重新获取视频信息: 1次 +2023-01-13 13:12:57.999 [INFO][MainProcess-10664-MainThread-19964][38] PushStreamService-start_service - 开始重新获取视频信息: 2次 +2023-01-13 15:43:43.762 [INFO][MainProcess-11488-MainThread-5440][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:43:44.777 [INFO][MainProcess-11488-MainThread-5440][38] PushStreamService-start_service - 开始重新获取视频信息: 0次 +2023-01-13 15:43:46.158 [INFO][MainProcess-11488-MainThread-5440][81] Cv2Utils-get_video_info - 视频信息, width:1920|height:1080|fps:25 +2023-01-13 15:44:00.788 [INFO][MainProcess-11488-MainThread-5440][224] Cv2Utils-read - 关闭拉流管道完成! +2023-01-13 15:45:16.962 [INFO][MainProcess-716-MainThread-1284][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:45:20.152 [ERROR][MainProcess-716-MainThread-1284][66] PushStreamService-start_service - 推流异常: 'int' object has no attribute 'start_push_stream' +2023-01-13 15:45:22.158 [ERROR][MainProcess-716-MainThread-1284][66] PushStreamService-start_service - 推流异常: 'int' object has no attribute 'start_push_stream' +2023-01-13 15:45:24.148 [ERROR][MainProcess-716-MainThread-1284][66] PushStreamService-start_service - 推流异常: 'int' object has no attribute 'start_push_stream' +2023-01-13 15:45:26.148 [ERROR][MainProcess-716-MainThread-1284][66] PushStreamService-start_service - 推流异常: 'int' object has no attribute 'start_push_stream' +2023-01-13 15:45:28.152 [ERROR][MainProcess-716-MainThread-1284][66] PushStreamService-start_service - 推流异常: 'int' object has no attribute 'start_push_stream' +2023-01-13 15:46:51.659 [INFO][MainProcess-28372-MainThread-8996][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:47:24.687 [INFO][MainProcess-18692-MainThread-32432][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:49:25.276 [INFO][MainProcess-28924-MainThread-3700][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:49:58.770 [INFO][MainProcess-17768-MainThread-33684][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:51:11.366 [INFO][MainProcess-26664-MainThread-22888][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:53:22.401 [INFO][MainProcess-20940-MainThread-32656][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:56:59.924 [INFO][MainProcess-11316-MainThread-10624][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 15:58:51.358 [INFO][MainProcess-12808-MainThread-30060][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 16:00:13.184 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:00:20.227 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:00:27.282 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:00:34.330 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:00:41.379 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:00:48.426 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:00:55.477 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:04:23.859 [ERROR][MainProcess-12808-MainThread-30060][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:07:51.939 [INFO][MainProcess-11556-MainThread-10732][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 16:07:58.992 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:08:06.046 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:08:13.100 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:08:35.323 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:08:42.366 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:08:49.417 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:08:56.464 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:09:54.054 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:01.103 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:08.159 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:15.207 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:22.259 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:29.304 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:36.349 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:43.406 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:50.448 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:10:57.489 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:04.534 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:11.586 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:18.630 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:25.679 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:32.727 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:39.772 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:46.822 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:11:53.870 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:00.915 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:07.973 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:15.023 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:22.065 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:29.120 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:36.174 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:43.226 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:50.270 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:12:57.321 [ERROR][MainProcess-11556-MainThread-10732][67] PushStreamService-start_service - 推流异常: Command '['ffprobe', '-show_format', '-show_streams', '-of', 'json', 'rtsp://127.0.0.1:8554/video']' timed out after 7 seconds +2023-01-13 16:13:04.146 [INFO][MainProcess-21980-MainThread-21248][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 16:13:11.200 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 0次 +2023-01-13 16:13:19.269 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 1次 +2023-01-13 16:13:27.331 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 2次 +2023-01-13 16:13:35.365 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 3次 +2023-01-13 16:13:43.424 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 4次 +2023-01-13 16:13:51.475 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 5次 +2023-01-13 16:13:59.527 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 6次 +2023-01-13 16:14:07.583 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 7次 +2023-01-13 16:14:15.629 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 8次 +2023-01-13 16:14:23.684 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 9次 +2023-01-13 16:14:31.743 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 10次 +2023-01-13 16:14:39.801 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 11次 +2023-01-13 16:14:47.855 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 12次 +2023-01-13 16:14:55.915 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 13次 +2023-01-13 16:15:03.975 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 14次 +2023-01-13 16:15:12.030 [INFO][MainProcess-21980-MainThread-21248][39] PushStreamService-start_service - 开始重新获取视频信息: 15次 +2023-01-13 16:15:18.469 [INFO][MainProcess-32128-MainThread-26468][27] PushStreamService-start_service - + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) + +2023-01-13 16:15:56.603 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 0次 +2023-01-13 16:16:04.664 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 1次 +2023-01-13 16:16:12.728 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 2次 +2023-01-13 16:16:20.796 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 3次 +2023-01-13 16:16:28.850 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 4次 +2023-01-13 16:16:36.923 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 5次 +2023-01-13 16:16:44.991 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 6次 +2023-01-13 16:16:53.052 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 7次 +2023-01-13 16:17:01.101 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 8次 +2023-01-13 16:17:09.159 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 9次 +2023-01-13 16:17:17.190 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 10次 +2023-01-13 16:17:25.240 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 11次 +2023-01-13 16:17:33.306 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 12次 +2023-01-13 16:17:41.361 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 13次 +2023-01-13 16:17:49.413 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 14次 +2023-01-13 16:17:57.463 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 15次 +2023-01-13 16:18:05.510 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 16次 +2023-01-13 16:18:13.558 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 17次 +2023-01-13 16:18:21.609 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 18次 +2023-01-13 16:21:03.797 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 19次 +2023-01-13 16:21:11.858 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 20次 +2023-01-13 16:21:19.916 [INFO][MainProcess-32128-MainThread-26468][39] PushStreamService-start_service - 开始重新获取视频信息: 21次 diff --git a/service/PushStreamService.py b/service/PushStreamService.py new file mode 100644 index 0000000..e88d350 --- /dev/null +++ b/service/PushStreamService.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +import time + +from alg_airport_ffmpeg.common.Constant import ConstantEnum +from alg_airport_ffmpeg.exception.CustomerException import ServiceException +from alg_airport_ffmpeg.util import YmlUtils, LogUtils +from loguru import logger + +from alg_airport_ffmpeg.util.Cv2Utils import Cv2Util + +''' + 推流服务 +''' + + +class PushStreamService: + + # 初始化 + def __init__(self): + # 获取airport环境所需要的配置 + self.__content = YmlUtils.get_configs() + # 初始化日志 + LogUtils.init_log(self.__content) + + # 服务调用启动方法 + def start_service(self): + logger.info(ConstantEnum.START_LOG.value[0]) + # 循环消息处理 + pull_url = self.__content["video"]["pullUrl"] + pushUrl = self.__content["video"]["pushUrl"] + cv2_tool = Cv2Util(pull_url, pushUrl) + cv2_init_num = 0 + while True: + try: + time1 = time.time() + #################################拉流推流一体################################### + # 1,检查拉流通道是否有流,没有流7秒后重试 + if not cv2_tool.is_video_stream(pull_url): + logger.info("开始重新获取视频信息: {}次", cv2_init_num) + time.sleep(1) + cv2_init_num += 1 + cv2_tool.close_push_stream() + continue + cv2_init_num = 0 + # 2. 推流存在跳过,不存在初始化推流 + cv2_tool.start_push_stream() + # 3. 判断推流通道是否有流,进程是否存在 + if cv2_tool.is_push_stream_ok(): + cv2_tool.close_push_stream() + #################################先拉流再推流################################### + # if cv2_tool.check_config(): + # time.sleep(1) + # logger.info("开始重新获取视频信息: {}次", cv2_init_num) + # cv2_init_num += 1 + # # cv2_tool.build_cv2() # 方式1 cv2 + # cv2_tool.get_video_info() # 方式2 ffmpeg + # continue + # cv2_init_num = 0 + # # frame = cv2_tool.cv2_read() # 方式1 cv2 + # frame = cv2_tool.read() # 方式2 ffmpeg + # if frame is None: + # continue + # cv2_tool.push_stream_write(frame) + # print(time.time()- time1) + except ServiceException as s: + logger.error("推流异常: {}", s.msg) + except Exception as e: + logger.error("推流异常: {}", e) + cv2_tool.close() + + + diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/service/__pycache__/PushStreamService.cpython-38.pyc b/service/__pycache__/PushStreamService.cpython-38.pyc new file mode 100644 index 0000000..86c3b09 Binary files /dev/null and b/service/__pycache__/PushStreamService.cpython-38.pyc differ diff --git a/service/__pycache__/__init__.cpython-38.pyc b/service/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..dcd14d9 Binary files /dev/null and b/service/__pycache__/__init__.cpython-38.pyc differ diff --git a/test/ffmpeg_test.py b/test/ffmpeg_test.py new file mode 100644 index 0000000..c1d59a3 --- /dev/null +++ b/test/ffmpeg_test.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- +import time +import cv2 +import subprocess as sp + +import ffmpeg +import numpy as np +from loguru import logger + + + + + +''' + 获取视频信息 +''' +def get_video_info(pullUrl): + try: + probe = ffmpeg.probe(pullUrl) + if probe is None or probe.get("streams") is None: + return + # 视频大小 + # format = probe['format'] + # size = int(format['size'])/1024/1024 + video_stream = next((stream for stream in probe['streams'] if stream.get('codec_type') == 'video'), None) + if video_stream is None: + logger.error("根据拉流地址未获取到视频流") + return + width = video_stream.get('width') + height = video_stream.get('height') + nb_frames = video_stream.get('nb_frames') + fps = video_stream.get('r_frame_rate') + # duration = video_stream.get('duration') + bit_rate = video_stream.get('bit_rate') + if width: + width = int(width) + if height: + height = int(height) + if nb_frames: + all_frames = int(nb_frames) + if fps: + up, down = str(fps).split('/') + fps = int(eval(up) / eval(down)) + # if duration: + # self.duration = float(video_stream['duration']) + if bit_rate: + bit_rate = int(bit_rate) / 1000 + logger.info("视频信息, width:{}|height:{}|fps:{}|all_frames:{}|bit_rate:{}", width, + height, fps, all_frames, bit_rate) + except ffmpeg._run.Error as er: + logger.error("获取视频信息异常: {}", er.stderr.decode(encoding='utf-8')) + except Exception as e: + logger.exception("获取视频信息异常:{}", e) + + +''' + 拉取视频 +''' +def build_pull_p(wh, pullUrl): + try: + command = [r'E:\liumeiti\ffmpeg\ffmpeg-master-latest-win64-gpl\bin\ffmpeg', + '-re', + '-y', + '-c:v', 'h264_cuvid', + '-resize', wh, + '-i', pullUrl, + '-f', 'rawvideo', + '-an', + '-'] + return sp.Popen(command, stdout=sp.PIPE) + except Exception as e: + logger.exception("构建拉流管道异常:{}", e) + return None + +def read(whr, whrz, pullUrl, h, w): + result = None + try: + in_bytes = build_pull_p(whrz, pullUrl).stdout.read(whr) + if in_bytes is not None and len(in_bytes) > 0: + # result = (np.frombuffer(in_bytes, np.uint8).reshape([int(self.height), int(self.width), 3])) + try: + img = (np.frombuffer(in_bytes, np.uint8)).reshape((h, w)) + except Exception as ei: + logger.exception("视频格式异常:{}", ei) + result = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12) + except Exception as e: + logger.exception("读流异常:{}", e) + return result + +def close(self): + if self.pull_p: + if self.pull_p.stdout: + self.pull_p.stdout.close() + self.pull_p.terminate() + self.pull_p.wait() + logger.info("关闭拉流管道完成, requestId:{}", self.requestId) + if self.p: + if self.p.stdin: + self.p.stdin.close() + self.p.terminate() + self.p.wait() + # self.p.communicate() + # self.p.kill() + logger.info("关闭管道完成, requestId:{}", self.requestId) + if self.or_video_file: + self.or_video_file.release() + logger.info("关闭原视频写入流完成, requestId:{}", self.requestId) + if self.ai_video_file: + self.ai_video_file.release() + logger.info("关闭AI视频写入流完成, requestId:{}", self.requestId) + + + +# async def push_stream_write(self, frame): +# self.p.stdin.write(frame.tostring()) +# +# async def push_stream(self, frame): +# if self.p is None: +# self.build_p() +# try: +# await self.push_stream_write(frame) +# return True +# except Exception as ex: +# logger.exception("推流进管道异常:{}, requestId: {}", ex, self.requestId) +# current_retry_num = 0 +# while True: +# try: +# time.sleep(1) +# self.p_push_retry_num += 1 +# current_retry_num += 1 +# if current_retry_num > 3 or self.p_push_retry_num > 600: +# return False +# self.build_p() +# await self.push_stream_write(frame) +# logger.info("构建p管道重试成功, 当前重试次数: {}, requestId: {}", current_retry_num, +# self.requestId) +# return True +# except Exception as e: +# logger.exception("构建p管道异常:{}, 开始重试, 当前重试次数:{}, requestId: {}", e, +# current_retry_num, self.requestId) +# return False + +# async def video_frame_write(self, or_frame, ai_frame): +# if or_frame is not None: +# self.or_video_file.write(or_frame) +# if ai_frame is not None: +# self.ai_video_file.write(ai_frame) + +# async def video_write(self, or_frame, ai_frame): +# try: +# self.build_write() +# if or_frame is not None and len(or_frame) > 0: +# await self.video_frame_write(or_frame, None) +# if ai_frame is not None and len(ai_frame) > 0: +# await self.video_frame_write(None, ai_frame) +# return True +# except Exception as ex: +# ai_retry_num = 0 +# while True: +# try: +# ai_retry_num += 1 +# if ai_retry_num > 3: +# logger.exception("重新写入离线分析后视频到本地,重试失败:{}, requestId: {}", e, self.requestId) +# return False +# if or_frame is not None and len(or_frame) > 0: +# await self.or_video_file.write(or_frame) +# if ai_frame is not None and len(ai_frame) > 0: +# await self.ai_video_file.write(ai_frame) +# logger.info("重新写入离线分析后视频到本地, 当前重试次数: {}, requestId: {}", ai_retry_num, +# self.requestId) +# return True +# except Exception as e: +# logger.exception("重新写入离线分析后视频到本地:{}, 开始重试, 当前重试次数:{}, requestId: {}", e, +# ai_retry_num, self.requestId) + +# def build_write(self): +# try: +# if self.fps is None or self.width is None or self.height is None: +# raise ServiceException(ExceptionType.VIDEO_CONFIG_EXCEPTION.value[0], +# ExceptionType.VIDEO_CONFIG_EXCEPTION.value[1]) +# if self.orFilePath is not None and self.or_video_file is None: +# self.or_video_file = cv2.VideoWriter(self.orFilePath, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, +# (int(self.wn), int(self.hn))) +# if self.or_video_file is None: +# raise ServiceException(ExceptionType.OR_WRITE_OBJECT_EXCEPTION.value[0], +# ExceptionType.OR_WRITE_OBJECT_EXCEPTION.value[1]) +# if self.aiFilePath is not None and self.ai_video_file is None: +# self.ai_video_file = cv2.VideoWriter(self.aiFilePath, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, +# (int(self.width), int(self.hn))) +# if self.ai_video_file is None: +# raise ServiceException(ExceptionType.AI_WRITE_OBJECT_EXCEPTION.value[0], +# ExceptionType.AI_WRITE_OBJECT_EXCEPTION.value[1]) +# except ServiceException as s: +# logger.exception("构建文件写对象异常: {}, requestId:{}", s, self.requestId) +# raise s +# except Exception as e: +# logger.exception("构建文件写对象异常: {}, requestId:{}", e, self.requestId) +# raise e + +# def video_merge(self, frame1, frame2): +# # frameLeft = cv2.resize(frame1, (int(self.width / 2), int(self.height / 2)), interpolation=cv2.INTER_LINEAR) +# # frameRight = cv2.resize(frame2, (int(self.width / 2), int(self.height / 2)), interpolation=cv2.INTER_LINEAR) +# # frame_merge = np.hstack((frameLeft, frameRight)) +# frame_merge = np.hstack((frame1, frame2)) +# return frame_merge +# +# def getP(self): +# if self.p is None: +# logger.error("获取管道为空, requestId:{}", self.requestId) +# raise ServiceException(ExceptionType.PULL_PIPELINE_INIT_EXCEPTION.value[0], +# ExceptionType.PULL_PIPELINE_INIT_EXCEPTION.value[1]) +# return self.p +# +# def getCap(self): +# if self.cap is None: +# logger.error("获取cv2为空, requestId:{}", self.requestId) +# raise ServiceException(ExceptionType.CV2_IS_NULL_EXCEPTION.value[0], +# ExceptionType.CV2_IS_NULL_EXCEPTION.value[1]) +# return self.cap +# +# def getOrVideoFile(self): +# if self.or_video_file is None: +# logger.error("获取原视频写入对象为空, requestId:{}", self.requestId) +# raise ServiceException(ExceptionType.OR_WRITE_OBJECT_EXCEPTION.value[0], +# ExceptionType.OR_WRITE_OBJECT_EXCEPTION.value[1]) +# return self.or_video_file +# +# def getAiVideoFile(self): +# if self.ai_video_file is None: +# logger.error("获取AI视频写入对象为空, requestId:{}", self.requestId) +# raise ServiceException(ExceptionType.AI_WRITE_OBJECT_EXCEPTION.value[0], +# ExceptionType.AI_WRITE_OBJECT_EXCEPTION.value[1]) +# return self.ai_video_file +if __name__== "__main__": + command = ['ffmpeg', + '-rtsp_transport', 'tcp', + '-i', 'rtsp://127.0.0.1:8554/video', # 指定输入文件 + '-c', 'copy', + '-f', 'flv', + "rtmp://192.168.10.101:19350/rlive/stream_9?sign=f8a15b6n"] + p = sp.Popen(command, shell=False) + while True: + time.sleep(2) + + print("pid", p.pid) + print("poll", p.poll()) + print("returncode", p.returncode) + # p.terminate() + # p.wait() diff --git a/test/strtest.py b/test/strtest.py new file mode 100644 index 0000000..241bc5f --- /dev/null +++ b/test/strtest.py @@ -0,0 +1,10 @@ + + +print(""" + _ ___ ____ ____ ___ ____ _____ + / \ |_ _| _ \| _ \ / _ \| _ \_ _| + / _ \ | || |_) | |_) | | | | |_) || | + / ___ \ | || _ <| __/| |_| | _ < | | + /_/ \_\___|_| \_\_| \___/|_| \_\|_| + :: AIRPORT SERVICE :: (1.0.0.RELEASE) +""") \ No newline at end of file diff --git a/util/Cv2Utils.py b/util/Cv2Utils.py new file mode 100644 index 0000000..0afa7fe --- /dev/null +++ b/util/Cv2Utils.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +import json +import subprocess as sp +import time + +import cv2 +import numpy as np +from loguru import logger + +from alg_airport_ffmpeg.enums.ExceptionEnum import ExceptionType +from alg_airport_ffmpeg.exception.CustomerException import ServiceException +from alg_airport_ffmpeg.concurrency.CommonThread import Common + +""" + 推流工具 +""" + + +class Cv2Util: + + def __init__(self, pullUrl=None, pushUrl=None): + self.__pullUrl = pullUrl + self.__pushUrl = pushUrl + self.__push_stream = None + self.__pull_stream = None + self.__width = None + self.__height = None + self.__wh = None + self.__fps = None + self.__cap = None + + def probe(self): + p = None + try: + args = ['ffprobe', '-show_format', '-show_streams', '-of', 'json', self.__pullUrl] + p = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE, close_fds=True) + out, err = p.communicate(timeout=7) + if p.returncode != 0: + # logger.error("获取视频信息异常: {}", err.stderr.decode(encoding='utf-8')) + return None + return json.loads(out.decode('utf-8')) + except Exception as e: + # logger.error("获取视频信息异常: {}", e) + return None + finally: + if p: + # if p.stdout: + # p.stdout.flush() + # p.stdout.close() + # if p.stderr: + # p.stderr.close() + p.terminate() + # parent_proc = psutil.Process(p.pid) + # for child_proc in parent_proc.children(recursive=True): + # child_proc.kill() + # parent_proc.kill() + # p.kill() + p.wait() + # logger.info("关闭获取视频管道完成!") + + # 获取视频信息 + def get_video_info(self): + try: + if self.__pullUrl is None or len(self.__pullUrl) == 0: + raise ServiceException(ExceptionType.PULL_STREAM_URL_EXCEPTION.value[0], + ExceptionType.PULL_STREAM_URL_EXCEPTION.value[1]) + probe = self.probe() + if probe is None or probe.get("streams") is None: + return + video_stream = next((stream for stream in probe['streams'] if stream.get('codec_type') == 'video'), None) + if video_stream is None: + return + width = video_stream.get('width') + height = video_stream.get('height') + fps = video_stream.get('r_frame_rate') + self.__width = int(width) + self.__height = int(height) + self.__wh = int(width * height * 3) + up, down = str(fps).split('/') + self.__fps = int(eval(up) / eval(down)) + logger.info("视频信息, width:{}|height:{}|fps:{}", self.__width, self.__height, self.__fps) + except ServiceException as s: + # logger.error("获取视频信息异常: {}", s.msg) + raise s + except Exception as e: + # logger.error("获取视频信息异常:{}", e) + raise e + + def build_cap(self, args): + try: + pullUrl = args[0] + return cv2.VideoCapture(pullUrl) + except Exception as e: + logger.error("初始化cap异常: {}", e) + return None + + # 构建 cv2 + def build_cv2(self): + try: + if self.__cap is not None: + # logger.info("重试, 关闭cap") + self.__cap.release() + self.__cap = None + self.__fps = None + self.__width = None + self.__height = None + if self.__pullUrl is None or len(self.__pullUrl) == 0: + raise ServiceException(ExceptionType.PULL_STREAM_URL_EXCEPTION.value[0], + ExceptionType.PULL_STREAM_URL_EXCEPTION.value[1]) + cap_thread = Common(timeout=7, func=self.build_cap, args=(self.__pullUrl,)) + cap_thread.setDaemon(True) + cap_thread.start() + self.__cap = cap_thread.get_result() + if self.__cap is None: + return + if self.__cap.isOpened(): + if self.__fps is None or self.__fps == 0: + self.__fps = int(self.__cap.get(cv2.CAP_PROP_FPS)) + if self.__width is None or self.__width == 0: + self.__width = int(self.__cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + if self.__height is None or self.__height == 0: + self.__height = int(self.__cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + logger.info("fps:{}|height:{}|width:{}", self.__fps, self.__height, self.__width) + except ServiceException as s: + logger.error("构建cv2异常: {}", s.msg) + raise s + except Exception as e: + logger.error("初始化cv2异常:{}", e) + raise e + + def cv2_read(self): + result = None + try: + if self.__cap is None: + self.build_cv2() + if self.__cap.isOpened(): + ret, frame = self.__cap.read() + if ret: + result = frame + del ret + del frame + except ServiceException as s: + raise s + except Exception as e: + logger.error("读流异常:{}", e) + raise e + finally: + if result is None: + self.__fps = None + self.__height = None + self.__width = None + if self.__cap: + self.__cap.release() + self.__cap = None + logger.info("关闭cv2!") + return result + + # 拉取视频 + def build_pull_stream(self): + try: + if self.__pullUrl is None or len(self.__pullUrl) == 0: + logger.error("拉流地址不能为空!") + raise ServiceException(ExceptionType.PULL_STREAM_URL_EXCEPTION.value[0], + ExceptionType.PULL_STREAM_URL_EXCEPTION.value[1]) + if self.__pull_stream: + logger.info("重试, 关闭拉流管道") + if self.__pull_stream.stdout: + self.__pull_stream.stdout.close() + self.__pull_stream.terminate() + self.__pull_stream.wait() + command = ['ffmpeg', + '-rtsp_transport', 'tcp', + '-i', self.__pullUrl, + '-f', 'rawvideo', + '-pix_fmt', 'bgr24', + '-an', + '-'] + # command = ['ffmpeg', + # '-re', + # '-y', + # '-c:v', 'h264_cuvid', + # '-resize', self.wah, + # '-i', self.pullUrl, + # '-f', 'rawvideo', + # '-an', + # '-'] + self.__pull_stream = sp.Popen(command, stdout=sp.PIPE) + except ServiceException as s: + logger.error("构建拉流管道异常: {}", s.msg) + raise s + except Exception as e: + logger.error("构建拉流管道异常:{}", e) + raise e + + def check_config(self): + if self.__fps is None or self.__width is None or self.__height is None: + return True + else: + return False + + def read(self): + result = None + try: + if self.__pull_stream is None: + self.build_pull_stream() + in_bytes = self.__pull_stream.stdout.read(self.__wh) + if in_bytes is not None and len(in_bytes) > 0: + result = (np.frombuffer(in_bytes, np.uint8).reshape([int(self.__height), int(self.__width), 3])) + except ServiceException as s: + raise s + except Exception as e: + logger.error("读流异常:{}", e) + raise e + finally: + if result is None: + self.__fps = None + self.__height = None + self.__width = None + if self.__pull_stream: + if self.__pull_stream.stdout: + self.__pull_stream.stdout.close() + self.__pull_stream.terminate() + self.__pull_stream.wait() + logger.info("关闭拉流管道完成!") + self.__pull_stream = None + return result + + # 关闭管道 + def close(self): + if self.__pull_stream: + if self.__pull_stream.stdout: + self.__pull_stream.stdout.close() + self.__pull_stream.terminate() + self.__pull_stream.wait() + logger.info("关闭拉流管道完成!") + if self.__push_stream: + if self.__push_stream.stdin: + self.__push_stream.stdin.close() + self.__push_stream.terminate() + self.__push_stream.wait() + logger.info("关闭推流管道完成!") + if self.__cap: + self.__cap.release() + + # 开始推流 + def build_push_stream(self): + try: + if self.__push_stream: + logger.info("重试, 关闭管道, 重新开启新管道") + if self.__push_stream.stdin: + self.__push_stream.stdin.close() + self.__push_stream.terminate() + self.__push_stream.wait() + if self.__pushUrl is None or len(self.__pushUrl) == 0: + logger.error("推流地址不能为空!") + raise ServiceException(ExceptionType.PUSH_STREAM_URL_EXCEPTION.value[0], + ExceptionType.PUSH_STREAM_URL_EXCEPTION.value[1]) + command = ['ffmpeg', + # '-loglevel', 'debug', + '-f', 'rawvideo', + '-vcodec', 'rawvideo', + '-pix_fmt', 'bgr24', + '-s', "{}x{}".format(int(self.__width), int(self.__height)), + '-r', str(self.__fps), + '-i', '-', + # '-g', str(self.__fps), + # '-maxrate', '15000k', + # '-minrate', '3000k', + # '-profile:v', 'high', + # '-level', '5.1', + # '-b:v', '4000k', + # '-crf', '26', + # '-bufsize', '4000k', + # '-c:v', 'libx264', + # '-tune', 'zerolatency', + # '-sc_threshold', '0', + # '-pix_fmt', 'yuv420p', + # "-an", + # '-preset', 'medium', # 指定输出的视频质量,会影响文件的生成速度,有以下几个可用的值 ultrafast, + # superfast, veryfast, faster, fast, medium, slow, slower, veryslow。 + ] + for url in self.__pushUrl: + command.extend(['-f', 'flv', + '-g', str(self.__fps), + '-maxrate', '15000k', + '-minrate', '3000k', + '-b:v', '4000k', + '-bufsize', '4000k', + '-c:v', 'libx264', + '-tune', 'zerolatency', + '-sc_threshold', '0', + '-pix_fmt', 'yuv420p', + '-preset', 'fast', + "-an", "-y", url + ]) + self.__push_stream = sp.Popen(command, stdin=sp.PIPE, shell=False) + except ServiceException as s: + logger.error("构建推流管道异常: {}", s.msg) + raise s + except Exception as e: + logger.error("初始化推流管道异常:{}", e) + raise e + + def push_stream_write(self, frame): + try: + if self.__push_stream is None: + self.build_push_stream() + self.__push_stream.stdin.write(frame.tostring()) + except Exception as ex: + logger.error("推流异常:{}", ex) + current_retry_num = 0 + while True: + try: + self.build_push_stream() + self.__push_stream.stdin.write(frame.tostring()) + logger.info("推流重试成功, 当前重试次数: {}", current_retry_num) + break + except Exception as e: + current_retry_num += 1 + logger.error("推流异常:{}, 开始重试, 当前重试次数:{}", e, current_retry_num) + time.sleep(1) + if current_retry_num > 1: + raise Exception("推流异常,请检查通道是否被占用!") + + def close_push_stream(self): + if self.__push_stream: + self.__push_stream.terminate() + self.__push_stream.wait() + self.__push_stream = None + + def is_push_stream_ok(self): + if self.__push_stream: + if self.__push_stream.poll() is not None: + return True + return False + + # 开始推流 + def start_push_stream(self): + try: + if self.__push_stream: + return + if self.__pullUrl is None or len(self.__pullUrl) == 0: + logger.error("拉流地址不能为空!") + raise ServiceException(ExceptionType.PUll_STREAM_URL_EXCEPTION.value[0], + ExceptionType.PUll_STREAM_URL_EXCEPTION.value[1]) + if self.__pushUrl is None or len(self.__pushUrl) == 0: + logger.error("推流地址不能为空!") + raise ServiceException(ExceptionType.PUSH_STREAM_URL_EXCEPTION.value[0], + ExceptionType.PUSH_STREAM_URL_EXCEPTION.value[1]) + command = ['ffmpeg', + '-re', + '-rtsp_transport', 'tcp', + '-i', self.__pullUrl, + ] + for url in self.__pushUrl: + command.extend(['-f', 'flv', + '-g', str(25), + '-c:v', 'copy', + "-an", "-y", url + ]) + self.__push_stream = sp.Popen(command, shell=False) + except ServiceException as s: + logger.error("构建推流管道异常: {}", s.msg) + raise s + except Exception as e: + logger.error("初始化推流管道异常:{}", e) + raise e + + def is_video_stream(self, url): + p = None + try: + if url is None or len(url) == 0: + raise Exception("流地址不能为空!") + args = ['ffprobe', '-show_format', '-show_streams', '-of', 'json', url] + p = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE, close_fds=True) + out, err = p.communicate(timeout=7) + if p.returncode != 0: + return False + probe = json.loads(out.decode('utf-8')) + if probe is None or probe.get("streams") is None: + return False + video_stream = next((stream for stream in probe['streams'] if stream.get('codec_type') == 'video'), None) + if video_stream is None: + return False + return True + except ServiceException as s: + raise s + except Exception as e: + return False + finally: + if p: + p.terminate() + p.wait() + p = None diff --git a/util/LogUtils.py b/util/LogUtils.py new file mode 100644 index 0000000..b301679 --- /dev/null +++ b/util/LogUtils.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +import sys +import os +from loguru import logger + + +# 初始化日志配置 +def init_log(content): + if not os.path.exists(content["log"]["base_path"]): + os.makedirs(content["log"]["base_path"]) + # 移除日志设置 + logger.remove(handler_id=None) + # 打印日志到文件 + if content["log"]["enable_file_log"]: + logger.add(content["log"]["base_path"] + content["log"]["log_name"], + rotation=content["log"]["rotation"], + retention=content["log"]["retention"], + format=content["log"]["log_fmt"], + level=content["log"]["level"], + enqueue=content["log"]["enqueue"], + encoding=content["log"]["encoding"]) + # 控制台输出 + if content["log"]["enable_stderr"]: + logger.add(sys.stderr, + format=content["log"]["log_fmt"], + level=content["log"]["level"], + enqueue=True) diff --git a/util/TimeUtils.py b/util/TimeUtils.py new file mode 100644 index 0000000..89adac5 --- /dev/null +++ b/util/TimeUtils.py @@ -0,0 +1,19 @@ +import time +import datetime + +YY_MM_DD_HH_MM_SS = "%Y-%m-%d %H:%M:%S" +YMDHMSF = "%Y%m%d%H%M%S%f" + + +def generate_timestamp(): + """根据当前时间获取时间戳,返回整数""" + return int(time.time()) + + +def now_date_to_str(fmt=None): + if fmt is None: + fmt = YY_MM_DD_HH_MM_SS + return datetime.datetime.now().strftime(fmt) + +if __name__=="__main__": + print(now_date_to_str(YMDHMSF)) diff --git a/util/YmlUtils.py b/util/YmlUtils.py new file mode 100644 index 0000000..5f06c71 --- /dev/null +++ b/util/YmlUtils.py @@ -0,0 +1,21 @@ +import os +import yaml +from alg_airport_ffmpeg.common.Constant import ConstantEnum + +""" + 获取配置项信息 +""" + + +def get_configs(): + print("开始读取配置文件,获取配置信息:", ConstantEnum.APPLICATION_CONFIG.value[0]) + config_path = os.path.abspath(ConstantEnum.APPLICATION_CONFIG.value[0]) + if not os.path.exists(config_path): + raise Exception("未找到配置文件:{}".format(ConstantEnum.APPLICATION_CONFIG.value[0])) + with open(config_path, ConstantEnum.R.value[0], encoding=ConstantEnum.UTF_8.value[0]) as f: + file_content = f.read() + content = yaml.load(file_content, yaml.FullLoader) + if not content: + raise Exception("配置项不能为空:{}".format(ConstantEnum.APPLICATION_CONFIG.value[0])) + print("读取配置文件完成!") + return content diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/util/__pycache__/Cv2Utils.cpython-38.pyc b/util/__pycache__/Cv2Utils.cpython-38.pyc new file mode 100644 index 0000000..63a7ac3 Binary files /dev/null and b/util/__pycache__/Cv2Utils.cpython-38.pyc differ diff --git a/util/__pycache__/LogUtils.cpython-38.pyc b/util/__pycache__/LogUtils.cpython-38.pyc new file mode 100644 index 0000000..2aedad4 Binary files /dev/null and b/util/__pycache__/LogUtils.cpython-38.pyc differ diff --git a/util/__pycache__/YmlUtils.cpython-38.pyc b/util/__pycache__/YmlUtils.cpython-38.pyc new file mode 100644 index 0000000..0e17d8d Binary files /dev/null and b/util/__pycache__/YmlUtils.cpython-38.pyc differ diff --git a/util/__pycache__/__init__.cpython-38.pyc b/util/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..1989150 Binary files /dev/null and b/util/__pycache__/__init__.cpython-38.pyc differ